<<<<<<< HEAD ======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
<<<<<<< HEAD =======

The downloaded binary packages are in
    /var/folders/9s/0zkjn8tm8xj7wp0059bl3v9000020t/T//RtmpiUigeM/downloaded_packages
>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be <<<<<<< HEAD ======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Loaded required libraries
libraries
knitr
FactoMineR
factoextra
pheatmap
gprofiler2
pheatmap
biomaRt
R version 4.0.2 (2020-06-22)
<<<<<<< HEAD
Platform: x86_64-conda_cos6-linux-gnu (64-bit)
Running under: CentOS Linux 7 (Core)

Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.0.2/lib/libopenblasp-r0.3.10.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
=======
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Mojave 10.14.6

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRblas.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
<<<<<<< HEAD
[1] biomaRt_2.46.3   pheatmap_1.0.12  factoextra_1.0.7 ggplot2_3.3.3    FactoMineR_2.3   knitr_1.30      

loaded via a namespace (and not attached):
 [1] ggrepel_0.9.1        Rcpp_1.0.6           lattice_0.20-41      prettyunits_1.1.1    assertthat_0.2.1     digest_0.6.27        utf8_1.2.1           BiocFileCache_1.14.0 R6_2.5.0             stats4_4.0.2         RSQLite_2.2.3        evaluate_0.14        highr_0.8            httr_1.4.2          
[15] pillar_1.5.1         rlang_0.4.10         progress_1.2.2       curl_4.3             blob_1.2.1           S4Vectors_0.28.1     rmarkdown_2.5        stringr_1.4.0        bit_4.0.4            munsell_0.5.0        compiler_4.0.2       xfun_0.20            askpass_1.1          pkgconfig_2.0.3     
[29] BiocGenerics_0.36.0  htmltools_0.5.1.1    openssl_1.4.3        flashClust_1.01-2    tidyselect_1.1.0     tibble_3.1.0         IRanges_2.24.1       XML_3.99-0.5         fansi_0.4.2          dbplyr_2.0.0         crayon_1.4.1         dplyr_1.0.5          withr_2.4.1          rappdirs_0.3.3      
[43] MASS_7.3-53          leaps_3.1            grid_4.0.2           gtable_0.3.0         lifecycle_1.0.0      DBI_1.1.1            magrittr_2.0.1       scales_1.1.1         stringi_1.5.3        debugme_1.1.0        cachem_1.0.4         scatterplot3d_0.3-41 xml2_1.3.2           ellipsis_0.3.1      
[57] generics_0.1.0       vctrs_0.3.6          RColorBrewer_1.1-2   tools_4.0.2          bit64_4.0.5          Biobase_2.50.0       glue_1.4.2           purrr_0.3.4          hms_1.0.0            parallel_4.0.2       fastmap_1.1.0        yaml_2.2.1           AnnotationDbi_1.52.0 colorspace_2.0-0    
[71] cluster_2.1.0        BiocManager_1.30.10  memoise_2.0.0       
======= [1] biomaRt_2.44.4 pheatmap_1.0.12 gprofiler2_0.2.0 factoextra_1.0.7 ggplot2_3.3.3 FactoMineR_2.4 knitr_1.32 loaded via a namespace (and not attached): [1] Biobase_2.48.0 httr_1.4.2 sass_0.3.1 tidyr_1.1.3 bit64_4.0.5 jsonlite_1.7.2 viridisLite_0.4.0 bslib_0.2.4 assertthat_0.2.1 askpass_1.1 highr_0.9 BiocFileCache_1.12.1 BiocManager_1.30.12 stats4_4.0.2 [15] blob_1.2.1 yaml_2.2.1 progress_1.2.2 ggrepel_0.9.1 pillar_1.6.0 RSQLite_2.2.6 lattice_0.20-41 glue_1.4.2 digest_0.6.27 RColorBrewer_1.1-2 colorspace_2.0-0 htmltools_0.5.1.1 XML_3.99-0.6 pkgconfig_2.0.3 [29] purrr_0.3.4 scales_1.1.1 tibble_3.1.0 openssl_1.4.3 generics_0.1.0 IRanges_2.22.2 ellipsis_0.3.1 DT_0.18 cachem_1.0.4 withr_2.4.1 BiocGenerics_0.34.0 lazyeval_0.2.2 magrittr_2.0.1 crayon_1.4.1 [43] memoise_2.0.0 evaluate_0.14 fansi_0.4.2 MASS_7.3-53.1 xml2_1.3.2 tools_4.0.2 data.table_1.14.0 prettyunits_1.1.1 hms_1.0.0 lifecycle_1.0.0 stringr_1.4.0 plotly_4.9.3 S4Vectors_0.26.1 munsell_0.5.0 [57] cluster_2.1.1 AnnotationDbi_1.50.3 flashClust_1.01-2 compiler_4.0.2 jquerylib_0.1.3 rlang_0.4.10 grid_4.0.2 rappdirs_0.3.3 htmlwidgets_1.5.3 leaps_3.1 rmarkdown_2.7 gtable_0.3.0 curl_4.3 DBI_1.1.1 [71] R6_2.5.0 dplyr_1.0.5 fastmap_1.1.0 bit_4.0.4 utf8_1.2.1 stringi_1.5.3 parallel_4.0.2 Rcpp_1.0.6 vctrs_0.3.7 dbplyr_2.1.1 scatterplot3d_0.3-41 tidyselect_1.1.0 xfun_0.22 >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

Synopsis du projet

Travail demandé

Le but de ce travail est de mettre en oeuvre les méthodes vues dans le module 3 “R et statistiques” pour explorer le jeu de données de Pavokovic, et de rendre un rapport d’analyse au format .Rmd.

Nous fournissons ci-dessous une trame avec les principales sections attendues. Certaines contiennent déjà du code. Vous devrez en compléter d’autres. Sentez-vous libres d’adapter cette trame ou d’y ajouter des analyses complémentaires si elles vous aident à interpréter vos résultats.

Remise du rapport

Date: le 10 mai 2021 minuit. Si vous anticipez un problème pour remettre le rapport à cette date contactez-nous aussi rapidement que possible pour que nous puissions prévoir une remise plus tardive.

    <<<<<<< HEAD
  • Commencez par renommer le fichier .Rmd en remplaçant Prenom-NOM par vos nom et prénom.

  • Le rapport est attendu en formats .Rmd + .HTML (en gardant l’option self_contained de l’en-tête activée).

  • =======
  • Commencez par renommer le fichier .Rmd en remplaçant Prenom-NOM par vos nom et prénom.
  • Le rapport est attendu en formats .Rmd + .HTML (en gardant l’option self_contained de l’en-tête activée).
  • >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
  • Déposez les fichiers dans un sous-dossier de vote compte du cluster. Attention, veillez à respecter précisément cette structure de chemin car nous nous baserons dessus pour récupérer vos résultats.

    /shared/projects/dubii2021/[login]/m3-stat-R/mini-projet

Critères d’évaluation

  • Reproductibilité des analyses: nous tenterons de regénérer le rapport HTML à partir de votre Rmd, en partant de notre compte sur le serveur IFB.
  • Manipulation des objets R
  • Mobilisation des méthodes statistiques vues au cours
  • Pertinence des interprétations statistiques
  • Pertinence des interprétations biologiques
  • Clarté de la rédaction
  • Clarté des illustrations (figures et tableaux): graphismes, légendes …

Nous vous encourageons à assurer la lisibilité de votre code (syntaxe, nommage des variables, commentaires de code)

Objectifs scientifiques

Nous partons du même jeu de données Fil Rouge de ce module issues de la publication Pavkovic, M., Pantano, L., Gerlach, C.V. et al. Multi omics analysis of fibrotic kidneys in two mouse models. Sci Data 6, 92 (2019). https://doi.org/10.1038/s41597-019-0095-5

Rappel sur les échantillons:

Deux modèles de fibrose rénale chez la souris sont étudiés:

  1. Le premier est un modèle de néphropathie réversible induite par l’acide folique (folic acid (FA)). Les souris ont été sacrifiées avant le traitement (normal), puis à jour 1, 2, 7 et 14 (day1,…) après une seule injection d’acide folique.

  2. Le second est un modèle irréversible induit chrirurgicalement (unilateral ureteral obstruction (UUO)). les souris ont été sacrifiées avant obstruction (day 0) et à 3, 7 et 14 jours après obstruction par ligation de l’uretère du rein gauche.

A partir de ces extraits de rein, l’ARN messager total et les petits ARNs ont été séquencés et les protéines caratérisées par spectrométrie de masse en tandem (TMT).

But scientifique: Dans le tutoriel sur les dataframes, vous avez travaillé sur les données de transcriptome du modèle UUO. Dans ce mini-projet, vous allez travailler sur les données du transcriptome du modèle FA afin de regrouper les observations (échantillon) et les gènes selon des profils d’expression similaires.

Votre projet se décompose en 4 parties:

  1. statsitiques descriptives des données brutes: commandes fournies
  2. normalisation des données : commandes fournies
  3. statistiques descriptives des données normalisées: à vous de jouer
  4. analyse de regroupement des données: à vous de jouer

1. Les données brutes

Vous n’avez rien à coder ici. Le code est fourni.

Chargement des données brutes

Le bloc suivant contient une fonction qui permet de télécharger un fichier dans l’espace de travail, sauf s’il est déjà présent. Nous l’utiliserons ensuite pour télécharger les données à analyser en évitant de refaire le transfert à chaque exécution de l’analyse.

<<<<<<< HEAD
#' @title Download a file only if it is not yet here
#' @author Jacques van Helden email{Jacques.van-Helden@@france-bioinformatique.fr}
#' @param url_base base of the URL, that will be prepended to the file name
#' @param file_name name of the file (should not contain any path)
#' @param local_folder path of a local folder where the file should be stored
#' @return the function returns the path of the local file, built from local_folder and file_name
#' @export©
download_only_once <- function(
  url_base, 
  file_name,
  local_folder) {

  ## Define the source URL  
  url <- file.path(url_base, file_name)
  message("Source URL\n\t",  url)

  ## Define the local file
  local_file <- file.path(local_folder, file_name)
  
  ## Create the local data folder if it does not exist
  dir.create(local_folder, showWarnings = FALSE, recursive = TRUE)
  
  ## Download the file ONLY if it is not already there
  if (!file.exists(local_file)) {
    message("Downloading file from source URL to local file\n\t", 
            local_file)
    download.file(url = url, destfile = local_file)
  } else {
    message("Local file already exists, no need to download\n\t", 
            local_file)
  }
  
  return(local_file)
}
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

Nous téléchargeons deux fichiers dans un dossier local ~/m3-stat-R/pavkovic_analysis (vous pouvez changer le nom ou chemin dans le chunk ci-dessous), et les chargeons dans les data.frames suivants:

  • Données brutes de transcriptome: fa_expr_raw
  • Métadonnées: fa_meta
<<<<<<< HEAD
## Define the remote URL and local folder
pavkovic_url <- "https://github.com/DU-Bii/module-3-Stat-R/raw/master/stat-R_2021/data/pavkovic_2019/"

## Define the local folder for this analysis (where the data will be downloaded and the results generated)
pavkovic_folder <- "~/m3-stat-R/pavkovic_analysis"

## Define a sub-folder for the data
pavkovic_data_folder <- file.path(pavkovic_folder, "data")

## Download and load the expression data table
## Note: we use check.names=FALSE to avoid replacing hyphens by dots
## in sample names, because we want to keep them as in the 
## original data files. 
message("Downloading FA transcriptome file\t", "fa_raw_counts.tsv.gz",
  "\n\tfrom\t", pavkovic_url)
fa_expr_file <- download_only_once(
  url_base = pavkovic_url, 
  file_name = "fa_raw_counts.tsv.gz",
  local_folder = pavkovic_data_folder)

## Load the expresdsion table
message("Loading FA transcriptome data from\n\t", fa_expr_file)
fa_expr_raw <- read.delim(file = fa_expr_file, 
                       header = TRUE, 
                       row.names = 1)

## Download the metadata file
message("Downloading FA metadata file\t", "fa_transcriptome_metadata.tsv",
  "\n\tfrom\t", pavkovic_url)
fa_meta_file <- download_only_once(
  url_base = pavkovic_url, 
  file_name = "fa_transcriptome_metadata.tsv",
  local_folder = pavkovic_data_folder)

## Load the metadata
message("Loading FA metadata from\n\t", fa_meta_file)
fa_meta <- read.delim(file = fa_meta_file, 
                       header = TRUE, 
                       row.names = 1)

Nous regardons la structure de chaque dataframe.

str(fa_expr_raw)
'data.frame':   46679 obs. of  18 variables:
=======

Nous regardons la structure de chaque dataframe.

'data.frame':   46679 obs. of  18 variables:
>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
 $ day1_1  : num  2278.8 0 36.3 13.2 0 ...
 $ day1_2  : num  1786.5 0 22.15 7.15 27.9 ...
 $ day1_3  : num  2368.62 0 39.48 1.12 6.9 ...
 $ day14_1 : num  627.758 0 14.471 0.867 5.692 ...
 $ day14_2 : num  559.2 0 10.2 0 1.9 ...
 $ day14_3 : num  611.434 0 31.691 0 0.655 ...
 $ day2_1  : num  2145.22 0 300.56 1.71 57.38 ...
 $ day2_2  : num  262.45 0 4.77 0 0 ...
 $ day2_3  : num  745.84 0 123.9 5.26 38.9 ...
 $ day3_1  : num  987.185 0 51.856 0.802 8.931 ...
 $ day3_2  : num  1077.65 0 8.43 0 6.97 ...
 $ day3_3  : num  1335.1 0 69.9 0 0 ...
 $ day7_1  : num  1096.08 0 6.67 0 7.94 ...
 $ day7_2  : num  1035.846 0 6.955 0.849 101.648 ...
 $ day7_3  : num  1090.04 0 42.58 1.71 0.65 ...
 $ normal_1: num  483.23 0 7.35 0.86 32.06 ...
 $ normal_2: num  1842.1 0 11.2 0 10.4 ...
 $ normal_3: num  475.7 0 1.03 0 0 ...
<<<<<<< HEAD
str(fa_meta)
'data.frame':   18 obs. of  5 variables:
=======

'data.frame':   18 obs. of  5 variables:
>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
 $ dataType    : chr  "transcriptome" "transcriptome" "transcriptome" "transcriptome" ...
 $ sampleName  : chr  "day14_1" "day14_2" "day14_3" "day1_1" ...
 $ condition   : chr  "day14" "day14" "day14" "day1" ...
 $ sampleNumber: int  1 2 3 1 2 3 1 2 3 1 ...
 $ color       : chr  "#FF4400" "#FF4400" "#FF4400" "#BBD7FF" ...

Les deux fichiers ne donnent pas les observations de l’échantillon dans le même ordre:

<<<<<<< HEAD
fa_meta$sampleName == names(fa_expr_raw)
 [1] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE

Nous les réorganisons les échantillons dans l’ordre de l’expérience: condition normale, puis day 1 à 14 avec les 3 réplicats.

sample_order <- c(paste(rep(c("normal", "day1", "day2", "day3", "day7", "day14"), each = 3),
                        1:3, sep = "_"))

fa_expr_raw <- fa_expr_raw[,sample_order]
fa_meta <- fa_meta[match(sample_order, fa_meta$sampleName),]

# View(fa_meta)
kable(fa_meta, caption = "Metdata for Pavkovoc FA transcriptome")
=======
 [1] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE

Nous les réorganisons les échantillons dans l’ordre de l’expérience: condition normale, puis day 1 à 14 avec les 3 réplicats.

>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Metdata for Pavkovoc FA transcriptome
dataType sampleName condition sampleNumber color
16 transcriptome normal_1 normal 1 #BBFFBB
17 transcriptome normal_2 normal 2 #BBFFBB
18 transcriptome normal_3 normal 3 #BBFFBB
4 transcriptome day1_1 day1 1 #BBD7FF
5 transcriptome day1_2 day1 2 #BBD7FF
6 transcriptome day1_3 day1 3 #BBD7FF
7 transcriptome day2_1 day2 1 #F0BBFF
8 transcriptome day2_2 day2 2 #F0BBFF
9 transcriptome day2_3 day2 3 #F0BBFF
10 transcriptome day3_1 day3 1 #FFFFDD
11 transcriptome day3_2 day3 2 #FFFFDD
12 transcriptome day3_3 day3 3 #FFFFDD
13 transcriptome day7_1 day7 1 #FFDD88
14 transcriptome day7_2 day7 2 #FFDD88
15 transcriptome day7_3 day7 3 #FFDD88
1 transcriptome day14_1 day14 1 #FF4400
2 transcriptome day14_2 day14 2 #FF4400
3 transcriptome day14_3 day14 3 #FF4400

=> Ainsi, nous avons un jeu de données avec un échantillon de 18 observations et des données d’expression de 46679 gènes.

Transformation log2

Appliquez une transformation log2 des données brutes, après avoir ajouté un epsilon \(\epsilon = 1\) (les valeurs nulles seront donc représentées par un log2(counts) valant \(0\). Stockez le résultat dans un data.frame nommé fa_expr_log2.

Affchez un fragment des tableaux fa_expr_raw et fa_expr_log2 en sélectionnant les lignes 100 à 109 et les colonnes 5 à 10, afin de vous assurer que la transformation log2 a bien fonctionné.

<<<<<<< HEAD
## Log2 transformation of the transcriptome data
epsilon <- 1
fa_expr_log2 <- log2(fa_expr_raw + epsilon)
# dim(fa_expr_log2)
# View(head(fa_expr_log2))

## Display of a fragment of the data before and after log2 transformation
kable(fa_expr_raw[100:109, 5:10], caption = "Fragment des données transcriptomiques brutes")
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Fragment des données transcriptomiques brutes
day1_2 day1_3 day2_1 day2_2 day2_3 day3_1
ENSMUSG00000000567 599.648075 304.093177 1052.689447 106.995584 347.13842 479.59911
ENSMUSG00000000568 1008.026306 1349.126716 818.257157 116.136417 3406.45030 766.09722
ENSMUSG00000000579 599.832586 500.735597 473.399472 36.258005 410.57787 347.05054
ENSMUSG00000000581 942.511039 744.646735 546.260344 87.788535 319.12679 461.45732
ENSMUSG00000000594 3063.845705 2743.404374 2283.051270 1115.588250 1491.61384 1576.68451
ENSMUSG00000000600 3341.854530 3561.805180 3674.188108 589.840068 1399.10751 3446.01856
ENSMUSG00000000605 791.312561 558.710943 489.579657 77.008213 256.00200 282.80077
ENSMUSG00000000606 4.785252 6.566374 3.970399 0.000000 138.48677 0.00000
ENSMUSG00000000617 15.839486 29.138902 30.228506 6.670500 18.27493 13.41152
ENSMUSG00000000627 36.673777 35.967747 46.297517 2.878275 17.03638 15.45854
<<<<<<< HEAD
kable(fa_expr_log2[100:109, 5:10], caption = "Fragment des données transcriptomiques après transformation log2")
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Fragment des données transcriptomiques après transformation log2
day1_2 day1_3 day2_1 day2_2 day2_3 day3_1
ENSMUSG00000000567 9.230376 8.253106 10.041234 6.754829 8.443517 8.908690
ENSMUSG00000000568 9.978748 10.398879 9.678173 6.872046 11.734477 9.583266
ENSMUSG00000000579 9.230819 8.970783 8.889959 5.219479 8.685022 8.443153
ENSMUSG00000000581 9.881896 9.542348 9.096084 6.472302 8.322500 8.853176
ENSMUSG00000000594 11.581599 11.422277 11.157379 10.124882 10.543625 10.623593
ENSMUSG00000000600 11.706865 11.798798 11.843602 9.206624 10.451322 11.751133
ENSMUSG00000000605 9.629926 9.128538 8.938344 6.285554 8.005636 8.148735
ENSMUSG00000000606 2.532380 2.919602 2.313362 0.000000 7.123984 0.000000
ENSMUSG00000000617 4.073776 4.913555 4.964792 2.939321 4.268654 3.849151
ENSMUSG00000000627 5.235489 5.208195 5.563693 1.955415 4.172838 4.040765

Statistiques descriptives

Dans le tutorial sur les dataframes sur le jeu de données “uuo” (relisez le corrigé), nous vous avons demandé de créer un data.frame qui collectera les statistiques par gène et par échantillon. Nous vous demandons de réaliser une étude similaire sur les données “FA”.

Par échantillon avant normalisation

Nous créons un data.frame nommé sample_stat_prenorm qui comportera une ligne par échantillon et une colonne par statistique. Nous calculons les statistiques suivantes sur les valeurs log2 d’expression de chaque échantillon:

  • moyenne
  • écart-type
  • intervalle inter-quartiles
  • premier quartile
  • médiane
  • troisième quartile
  • maximum
  • nombre de valeurs nulles

Il sera affiché avec la fonction kable() (n’oubliez pas la légende).

<<<<<<< HEAD
sample_stat_prenorm <- data.frame(
  mean = apply(fa_expr_log2, 2, mean, na.rm=TRUE),
  sd = apply(fa_expr_log2, 2, sd, na.rm=TRUE),
  iqr = apply(fa_expr_log2, 2, IQR, na.rm=TRUE),
  Q1 = apply(fa_expr_log2, 2, quantile, p = 0.25, na.rm=TRUE),
  median = apply(fa_expr_log2, 2, median, na.rm=TRUE),
  Q3 = apply(fa_expr_log2, 2, quantile, p = 0.75, na.rm=TRUE),
  max = apply(fa_expr_log2, 2, max, na.rm=TRUE),
  null = apply(fa_expr_log2 == 0, 2, sum, na.rm=TRUE)
)

kable(sample_stat_prenorm, caption = "Sample-wise statistics before normalisation.")
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Sample-wise statistics before normalisation.
mean sd iqr Q1 median Q3 max null
normal_1 2.876096 3.382687 5.566333 0 1.115495 5.566333 23.48952 21415
normal_2 3.475984 3.851531 6.702346 0 1.991260 6.702346 24.43355 20203
normal_3 2.796962 3.301372 5.419783 0 1.057748 5.419783 23.47218 21660
day1_1 3.003791 3.526143 5.901203 0 1.074190 5.901203 23.65130 21700
day1_2 3.516314 3.877108 6.816493 0 2.019190 6.816493 24.58225 20152
day1_3 3.607632 3.917601 6.946987 0 2.252981 6.946987 24.70005 19621
day2_1 3.608242 3.944769 7.000535 0 2.170400 7.000535 24.32636 19978
day2_2 2.088503 2.832614 3.958139 0 0.000000 3.958139 21.94520 24446
day2_3 3.071767 3.573520 6.008117 0 1.309292 6.008117 23.47538 21455
day3_1 3.233264 3.638371 6.300924 0 1.621087 6.300924 23.41316 20772
day3_2 3.163046 3.595828 6.231985 0 1.556653 6.231985 23.03797 21142
day3_3 3.440775 3.777905 6.727257 0 1.991235 6.727257 23.97460 20287
day7_1 3.296409 3.667439 6.515860 0 1.703173 6.515860 23.34983 20865
day7_2 3.251819 3.587848 6.350051 0 1.822801 6.350051 23.20695 20467
day7_3 3.289691 3.617495 6.400883 0 1.954087 6.400883 23.53623 20045
day14_1 3.102341 3.484017 6.044672 0 1.597272 6.044672 23.41418 20496
day14_2 3.147517 3.557991 6.130466 0 1.600695 6.130466 23.87931 20870
day14_3 3.192445 3.533419 6.250143 0 1.699228 6.250143 23.46460 20396

Par gène avant normalisation

Nous créons ci-dessous un data.frame nommé gene_stat_prenorm qui comportera une ligne par gène et une colonne par statistique. Nous calculons les statistiques suivantes sur les valeurs log2 de chaque gène.

  • moyenne
  • médiane
  • écart-type
  • premier quartile
  • troisième quartile
  • maximum
  • nombre de valeurs nulles
  • intervalle inter-quartiles

Ces résultats seront stockés dans un data.frame avec 1 ligne par échantillon et 1 colonne par statistique. Vous afficherez les lignes 100 à 109 de ce tableau de statistiques avec la fonction kable() (n’oubliez pas la légende).

<<<<<<< HEAD
## Gene-wise statistics before normalisation
gene_stat_prenorm <- data.frame(
  mean = apply(fa_expr_raw, 1, mean, na.rm = TRUE),
  sd = apply(fa_expr_raw, 1, sd, na.rm = TRUE),
  iqr = apply(fa_expr_raw, 1, IQR, na.rm = TRUE),
  Q1 = apply(fa_expr_raw, 1, quantile, p = 0.25, na.rm = TRUE),
  median = apply(fa_expr_raw, 1, median, na.rm = TRUE),
  Q3 = apply(fa_expr_raw, 1, quantile, p = 0.75, na.rm = TRUE),
  max = apply(fa_expr_raw, 1, max, na.rm = TRUE),
  null = apply(fa_expr_raw == 0, 1, sum, na.rm = TRUE)
)

kable(gene_stat_prenorm[100:109, ], caption = "Gene-wise statistics before normalisation")
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Gene-wise statistics before normalisation
mean sd iqr Q1 median Q3 max null
ENSMUSG00000000567 320.336744 286.399084 364.362298 91.046357 261.659973 455.40866 1052.68945 0
ENSMUSG00000000568 857.348825 694.432189 282.978949 575.571199 711.096105 858.55015 3406.45030 0
ENSMUSG00000000579 422.075702 162.290415 157.732606 340.417017 447.572742 498.14962 728.51737 0
ENSMUSG00000000581 498.978227 248.612029 259.324338 320.599085 471.269224 579.92342 942.51104 0
ENSMUSG00000000594 2113.558334 1237.521694 937.571105 1400.226182 1868.718762 2337.79729 6408.96044 0
ENSMUSG00000000600 2299.978163 911.222891 1480.192582 1719.874980 2093.572929 3200.06756 3674.18811 0
ENSMUSG00000000605 325.552118 169.456624 195.879561 221.735904 282.608205 417.61546 791.31256 0
ENSMUSG00000000606 9.929258 32.151350 2.736779 1.178311 1.934744 3.91509 138.48677 4
ENSMUSG00000000617 18.804116 9.470819 13.654701 12.188276 18.150391 25.84298 36.00235 0
ENSMUSG00000000627 15.623306 14.341852 12.308127 4.780913 10.064895 17.08904 46.29752 0

2. Filtrage et normalisation des données

Vous n’avez rien à coder ici. Le code est fourni.

Il existe plusieurs façons de normaliser les données de transcriptome vues dans les modules 4 et 5 (cf. total counts, quantiles, TMM, RLE, limma voom,…), mais nous avons choisi ici une solution simple tout en étant robuste pour normaliser les données en standardisant le 3ème quantile.

La méthode choisie ici consiste à :

  • écarter les gènes “non-détectés”, c’est-à-dire ceux ayant des valeurs nulles dans au moins 90% des échantillons;

  • écarter les gènes à peine exprimés, c’est-à-dire ceux ayant une valeur moyenne < 10 (arbitrairement);

  • standardiser les échantillons sur le 3ème quartile des valeurs non-nulles: on divise par le 3ème quartile de l’échantillon et on multiplie par le 3ème quartile déterminé sur l’ensemble des échantillons.

Nous fournissons ci-dessous le code.

<<<<<<< HEAD

Filtrage 1 : élimination des gènes non détectés ou à peine exprimés

## Data filtering: genes having at least 90% null values
undetected_genes <- gene_stat_prenorm$null >= ncol(fa_expr_raw) * 0.9
print(paste0("Undetected genes (null in >= 90% samples): ", sum(undetected_genes)))
[1] "Undetected genes (null in >= 90% samples): 14380"
## Data filtering: genes having a mean expression < 10
barely_expressed_genes <- gene_stat_prenorm$mean < 10
print(paste0("Barely expressed genes (mean < 10): ", sum(barely_expressed_genes)))
[1] "Barely expressed genes (mean < 10): 26286"
## Apply filtering on both criteria
discarded_genes <- undetected_genes | barely_expressed_genes
print(paste0("Discarded genes: ", sum(discarded_genes)))
[1] "Discarded genes: 26288"
kept_genes <- !discarded_genes
print(paste0("Kept genes: ", sum(kept_genes)))
[1] "Kept genes: 20391"
## Genes after filtering
fa_expr_log2_filtered <- fa_expr_log2[kept_genes, ]

Normalisation entre échantillons

## Generate a data frame where null values are replaced by NA
fa_expr_nonull <- fa_expr_log2_filtered
fa_expr_nonull[fa_expr_log2_filtered <= 0] <- NA
sum(is.na(fa_expr_nonull))
[1] 5598
## Compute the 3rd quartile of non-null values for each sample and store them in a vector:
sample_q3_nonull <- apply(fa_expr_nonull, 2, quantile, prob = 0.75, na.rm = TRUE)
# print(sample_q3_nonull)

## Compute the Q3 for all the values, which will serve as target value for the standardised sample Q3
all_q3_nonull <- quantile(unlist(fa_expr_nonull), prob = 0.75, na.rm = TRUE)
# print(all_q3_nonull)

## Standardise expression on the third quartile of non-null values
## Beware : for this standardization we keep the null values
## Trick : we transpose the table to apply the ratio sample per sample, 
## and then transpose the results to get back the genes in rows and samples in columns
fa_expr_log2_standard <- t(t(fa_expr_log2_filtered) * all_q3_nonull / sample_q3_nonull )
# quantile(unlist(fa_expr_log2_standard), probs = 0.75, na.rm = TRUE)

## We also compute the values for the "nonull" table for 
## the sake of comparison and to check that the third quantiles of non-null 
## values are well identical across samples.
fa_expr_log2_standard_nonull <- t(t(fa_expr_nonull) * all_q3_nonull / sample_q3_nonull )
# quantile(unlist(fa_expr_log2_standard_nonull), probs = 0.75, na.rm = TRUE)

## Compute Q3 before and after standardisation, including or not the null values
standardisation_impact <- data.frame(
  before_all = apply(fa_expr_log2_filtered, 2, quantile, prob =  0.75, na.rm = TRUE),
  before_nonull = apply(fa_expr_nonull, 2, quantile, prob =  0.75, na.rm = TRUE),
  after_nonul = apply(fa_expr_log2_standard_nonull, 2, quantile, prob =  0.75, na.rm = TRUE),
  after_all = apply(fa_expr_log2_standard, 2, quantile, prob =  0.75, na.rm = TRUE)
)

## Note: after standardization the Q3 of the data show some variations 
## because we compute them here with the null values
kable(standardisation_impact, caption = "Impact of standardization on the third quantile (Q3) per sample. Third quantiles are computed before and after standardisation, with either all the values of the filtered table, or only the non-null values. ")
=======

Normalisation entre échantillons

[1] 5598
## Compute the 3rd quartile of non-null values for each sample and store them in a vector:
sample_q3_nonull <- apply(fa_expr_nonull, 2, quantile, prob = 0.75, na.rm = TRUE)
# print(sample_q3_nonull)

## Compute the Q3 for all the values, which will serve as target value for the standardised sample Q3
all_q3_nonull <- quantile(unlist(fa_expr_nonull), prob = 0.75, na.rm = TRUE)
# print(all_q3_nonull)

## Standardise expression on the third quartile of non-null values
## Beware : for this standardization we keep the null values
## Trick : we transpose the table to apply the ratio sample per sample, 
## and then transpose the results to get back the genes in rows and samples in columns
fa_expr_log2_standard <- t(t(fa_expr_log2_filtered) * all_q3_nonull / sample_q3_nonull )
# quantile(unlist(fa_expr_log2_standard), probs = 0.75, na.rm = TRUE)

## We also compute the values for the "nonull" table for 
## the sake of comparison and to check that the third quantiles of non-null 
## values are well identical across samples.
fa_expr_log2_standard_nonull <- t(t(fa_expr_nonull) * all_q3_nonull / sample_q3_nonull )
# quantile(unlist(fa_expr_log2_standard_nonull), probs = 0.75, na.rm = TRUE)

## Compute Q3 before and after standardisation, including or not the null values
standardisation_impact <- data.frame(
  before_all = apply(fa_expr_log2_filtered, 2, quantile, prob =  0.75, na.rm = TRUE),
  before_nonull = apply(fa_expr_nonull, 2, quantile, prob =  0.75, na.rm = TRUE),
  after_nonul = apply(fa_expr_log2_standard_nonull, 2, quantile, prob =  0.75, na.rm = TRUE),
  after_all = apply(fa_expr_log2_standard, 2, quantile, prob =  0.75, na.rm = TRUE)
)

## Note: after standardization the Q3 of the data show some variations 
## because we compute them here with the null values
kable(standardisation_impact, caption = "Impact of standardization on the third quantile (Q3) per sample. Third quantiles are computed before and after standardisation, with either all the values of the filtered table, or only the non-null values. ")
>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Impact of standardization on the third quantile (Q3) per sample. Third quantiles are computed before and after standardisation, with either all the values of the filtered table, or only the non-null values.
before_all before_nonull after_nonul after_all
normal_1 7.892971 7.929471 8.546124 8.506785
normal_2 9.095259 9.120100 8.546124 8.522846
normal_3 7.690496 7.728764 8.546124 8.503808
day1_1 8.275875 8.310843 8.546124 8.510165
day1_2 9.215737 9.236819 8.546124 8.526618
day1_3 9.339495 9.356787 8.546124 8.530330
day2_1 9.372222 9.391459 8.546124 8.528618
day2_2 6.401122 6.563217 8.546124 8.335056
day2_3 8.428494 8.462042 8.546124 8.512242
day3_1 8.600914 8.618327 8.546124 8.528857
day3_2 8.449052 8.476090 8.546124 8.518862
day3_3 8.929730 8.945829 8.546124 8.530744
day7_1 8.637420 8.652095 8.546124 8.531628
day7_2 8.457495 8.478531 8.546124 8.524920
day7_3 8.568555 8.585032 8.546124 8.529721
day14_1 8.181662 8.194184 8.546124 8.533064
day14_2 8.339087 8.364105 8.546124 8.520562
day14_3 8.313689 8.334105 8.546124 8.525188

3. Les données normalisées

A vous de jouer!

Statistiques par gène après normalisation

Générez un data.frame avec une ligne par gène à partir du tableau de données normalisées, avec les statistiques suivantes (une statistique par colonne):

  • moyenne
  • variance
  • écart-type
  • coefficient de variation (écart-type divisé par la moyenne)
  • intervalle inter-quartiles
  • minimum
  • médiane
  • maximum
<<<<<<< HEAD
## Gene-wise statistics after normalisation
gene_stat_norm <- data.frame(
  mean = apply(fa_expr_log2_standard, 1, mean, na.rm = F),
  var = apply(fa_expr_log2_standard, 1, var, na.rm = TRUE),
  sd = apply(fa_expr_log2_standard, 1, sd, na.rm = F),
  CV = NA,
  min = apply(fa_expr_log2_standard, 1, min, na.rm = TRUE),
  Q1 = apply(fa_expr_log2_standard, 1, quantile, p = 0.25, na.rm = TRUE),
  median = apply(fa_expr_log2_standard, 1, median, na.rm = TRUE),
  Q3 = apply(fa_expr_log2_standard, 1, quantile, p = 0.75, na.rm = TRUE),
  max = apply(fa_expr_log2_standard, 1, max, na.rm = TRUE),
  null = apply(fa_expr_log2_standard == 0, 1, sum, na.rm = TRUE)
  )
gene_stat_norm$CV <- gene_stat_norm$sd / gene_stat_norm$mean
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

Annotation des gènes

Chaque gène étant donné par son identifiant dans la base de données ENSEMBL vous utiliserez le paquet biomaRt de bioconductor pour ajouter des annotations : symbole, chromosome, coordonnées génomiques, brin. Suivez pas à pas la méthode proposée:

  • sélectionnez la base de données ENSEMBL avec la fonction useMart(). Attention à choisir le bon génome avec l’agument dataset: mmusculus_gene_ensembl

  • avec la fonction getBM() récupérez de la base de données ENSEMBL les champs demandés (pour symbole utilisez external_gene_name) en appliquant “ensembl_geneid” pour l’agument filter et en indiquant pour l’argument values le vecteur des identifiants des gènes présents dans le dataframe gene_stat_norm. Vous obtenez un dataframe.

<<<<<<< HEAD

A présent, ajoutez au dataframe gene_stat_norm en 1ères colonnes les annotations retrouvées grâce à biomaRt. Attention, certains gènes ne sont pas retrouvés dans la version d’ENSEMBL sur biomaRt donc laissez des NA comme données manquantes dans ce cas. Nous vous recommandons d’utiliser la function merge() ou left_join() de dplyr pour fusionner les deux dataframes en un seul.

ensembl <- useMart("ENSEMBL_MART_ENSEMBL", host = "www.ensembl.org", dataset = "mmusculus_gene_ensembl")

genes <-getBM(attributes = c("ensembl_gene_id", "external_gene_name", "chromosome_name",
                              "start_position", "end_position", "strand"), 
                filter = "ensembl_gene_id",
                values = row.names(gene_stat_norm),
                mart = ensembl
              )
  
 
gene_stat_norm <- merge(genes, gene_stat_norm, by.x = "ensembl_gene_id", by.y = 0, sort = FALSE)

kable(gene_stat_norm[100:109, ], caption = "Gene-wise statistics after normalisation")
=======

A présent, ajoutez au dataframe gene_stat_norm en 1ères colonnes les annotations retrouvées grâce à biomaRt. Attention, certains gènes ne sont pas retrouvés dans la version d’ENSEMBL sur biomaRt donc laissez des NA comme données manquantes dans ce cas. Nous vous recommandons d’utiliser la function merge() ou left_join() de dplyr pour fusionner les deux dataframes en un seul.

Gene-wise statistics after normalisation
>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Gene-wise statistics after normalisation
ensembl_gene_id external_gene_name chromosome_name start_position end_position strand mean var sd CV min Q1 median Q3 max null
100 ENSMUSG00000000724 Cryba1 11 77609441 77616109 -1 5.595006 0.1972841 0.4441668 0.0793863 4.816940 5.423046 5.632564 5.829410 6.426584 0
101 ENSMUSG00000000731 Aire 10 77865856 77879444 -1 4.936591 0.5868326 0.7660500 0.1551779 3.715427 4.333682 4.984400 5.510108 6.247104 0
102 ENSMUSG00000000732 Icosl 10 77905136 77919747 1 5.543710 2.5859031 1.6080743 0.2900719 2.534509 4.617496 5.736471 6.856687 8.250127 0
103 ENSMUSG00000000738 Spg7 8 123789681 123824499 1 8.426883 0.2746445 0.5240653 0.0621897 7.477862 8.245098 8.330412 8.699996 9.484120 0
104 ENSMUSG00000000740 Rpl13 8 123829089 123831983 1 10.820838 0.1330747 0.3647941 0.0337122 10.156481 10.561094 10.850259 11.066164 11.622910 0
105 ENSMUSG00000000743 Chmp1a 8 123931003 123939502 -1 7.619693 1.1794074 1.0860052 0.1425261 6.110121 7.070298 7.398740 7.908298 10.866373 0
106 ENSMUSG00000000751 Rpa1 11 75188992 75239150 -1 7.368976 0.5226199 0.7229245 0.0981038 5.984878 7.065193 7.241989 7.676944 9.010795 0
107 ENSMUSG00000000753 Serpinf1 11 75300595 75313527 -1 2.830650 2.9689424 1.7230619 0.6087160 0.000000 2.595079 3.131414 3.959656 5.177214 4
108 ENSMUSG00000000759 Tubgcp3 8 12664277 12722248 -1 8.617902 0.3027336 0.5502123 0.0638453 7.904374 8.308391 8.497898 8.791621 10.039692 0
109 ENSMUSG00000000766 Oprm1 10 6708506 6988198 1 5.356086 0.6398959 0.7999349 0.1493506 3.297268 4.832527 5.519335 5.837577 6.401143 0

Challenge falcultatif:

Enfin, réordonnez les gènes par position génomique et affichez les lignes 5 premières et 5 dernières lignes de ce tableau de statistiques.

<<<<<<< HEAD
gene_stat_norm <- gene_stat_norm[order(gene_stat_norm$chromosome_name, gene_stat_norm$start_position),]

kable(gene_stat_norm[c(1:5, (nrow(gene_stat_norm)-4):nrow(gene_stat_norm)), ], caption = "Gene-wise statistics after normalisation.")
=======
Gene-wise statistics after normalisation.
>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Gene-wise statistics after normalisation.
ensembl_gene_id external_gene_name chromosome_name start_position end_position strand mean var sd CV min Q1 median Q3 max null
11991 ENSMUSG00000051951 Xkr4 1 3276124 3741721 -1 4.779755 1.5566203 1.2476459 0.2610272 2.624881 3.8434294 4.769964 5.895264 6.801471 0
17939 ENSMUSG00000103377 Gm37180 1 3435954 3438772 -1 2.371001 4.1080702 2.0268375 0.8548445 0.000000 0.2492876 1.809025 4.227908 5.706440 5
17870 ENSMUSG00000103161 Gm38148 1 3663115 3666126 -1 6.987419 0.2946434 0.5428107 0.0776840 6.414353 6.7053245 6.979361 7.133402 8.854627 0
17551 ENSMUSG00000102331 Gm19938 1 3717532 3729127 -1 4.711302 0.6605703 0.8127548 0.1725117 2.812792 4.5093438 4.750160 4.986285 6.770961 0
17647 ENSMUSG00000102592 Gm38385 1 3822233 3824583 1 5.854525 0.6717783 0.8196208 0.1399978 4.659168 5.2800025 5.756853 6.210068 8.211908 0
17896 ENSMUSG00000103234 Gm37158 Y 16691330 16697033 -1 11.652663 0.1297206 0.3601674 0.0309086 11.198733 11.3190509 11.590467 11.964794 12.385978 0
18295 ENSMUSG00000104381 Gm37032 Y 40144240 40147214 1 2.826286 3.2938714 1.8149026 0.6421511 0.000000 2.3552767 2.888919 3.775050 7.017864 3
17372 ENSMUSG00000101106 Gm28819 Y 68997874 69008469 1 2.663406 2.7687782 1.6639646 0.6247506 0.000000 2.1148791 2.549968 3.457991 6.756537 2
16671 ENSMUSG00000096768 Gm47283 Y 90796007 90827734 1 8.509179 0.7688016 0.8768133 0.1030432 7.211906 7.7011795 8.531591 9.082058 10.206865 0
17213 ENSMUSG00000099871 Gm21742 Y 90848682 90855309 1 2.162429 3.6862082 1.9199500 0.8878674 0.000000 1.0430928 1.815921 3.214804 6.394224 4

Distribution des données

  • Dessinez sous forme d’un histogramme la distribution des valeurs après normalisation (tous échantillons confondus)
<<<<<<< HEAD
hist(unlist(fa_expr_log2_standard), 
     breaks = seq(from = 0, to = max(fa_expr_log2_standard) + 1, by = 0.25),
     xlab = "log2(counts) after standardisation", 
     ylab = "number of genes after filtering",
     col = "#BBDDFF",
     las = 1, cex.axis = 0.8,
     main = "distribution after standardisation")
abline(v = mean(fa_expr_log2_standard), col = "darkgreen", lwd = 2)
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Distribution of expression values (log2 counts) after gene filtering and standardisation on the sample-wise third-quartile of non-null values. The vertical line highlights the mean value.

Distribution of expression values (log2 counts) after gene filtering and standardisation on the sample-wise third-quartile of non-null values. The vertical line highlights the mean value.

  • Dessinez un box plot des échantillons avant et après normalisation, et commentez la façon dont l’effet de la normalisation apparaît sur ces graphiques.
<<<<<<< HEAD
#### Box plots to show normalisation impact ####
par(mar = c(4,6,4,1)) ## Set the margins
par(mfrow = c(2,2))
boxplot(fa_expr_log2_filtered, 
        horizontal = TRUE,
        xlab = "log2(counts)", 
        las = 1, 
        col = fa_meta$color, 
        main = "Before standardisation\nall values")
boxplot(fa_expr_nonull, 
        horizontal = TRUE, 
        xlab = "log2(counts)", 
        las = 1, 
        col = fa_meta$color, 
        main = "Before standardisation\nzeros discarded")
boxplot(fa_expr_log2_standard_nonull, 
        horizontal = TRUE, 
        xlab = "log2(counts)", 
        las = 1, 
        col = fa_meta$color, 
        main = "Standardised\nzeros discarded")
boxplot(fa_expr_log2_standard, 
        xlab = "log2(counts)", 
        las = 1, 
        horizontal = TRUE, 
        col = fa_meta$color, 
        main = "Standardised\nall values")
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
Box plots showing the impact of normalisation

Box plots showing the impact of normalisation

<<<<<<< HEAD
par(mfrow = c(1, 1))
par(mar = c(4,5,5,1))
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

4. Analyse de regroupement des données

<<<<<<< HEAD

Filtrage 2 : sélection de gènes d’expression élevée et variable

Pour réduire le nombre de gènes, nous allons écarter les gènes faiblement exprimés (log2 moyen inférieur à 4), et ne retenir que ceux qui montrent des variations importantes entre échantillons. Pour ce dernier critère, nous nous basons sur le coefficient de variation afin de relativiser la dispersion (écart type) par rapport à la tendance centrale (moyenne).

Sélectionnez les gènes ayant un niveau log2 moyen minimal supérieur à 3 (\(s > 3\)) et un coefficient de variation supérieur à 0.5 (\(CV > 0.5\)). Note: ces valeurs sont parfaitement arbitraires, elles ont été choisies pour obtenir un nombre raisonnable de gènes.

## Compute a Boolean vector indicating whether each gene passes or not the expression level threshold
high_expression <- gene_stat_norm$mean > 3
# table(high_expression) # count number of genes with high/weak expression

## Compute a Boolean vector indicating whether each gene passes or not the variation coefficient threshold
high_variation <- gene_stat_norm$CV > 0.5
# table(high_variation) # count number of genes with weak high coeffficient of variation

## Compute a Boolean vector indicating whether each gene passes or not the variance threshold
# high_variance <- gene_stat_norm$var > 2
# table(high_variance) # count number of genes with weak high variance

## Select genes having both a high mean expression and a high variation coefficien
selected_genes <- high_variation & high_expression
# table(selected_genes) # count number of genes with weak high coeffficient of variation
print(paste0("Selected genes: ", sum(selected_genes)))
[1] "Selected genes: 473"
## Create a data frame with the expression of the selected genes
fa_expr_selected <- fa_expr_log2_standard[selected_genes, ]

Dessinez des histogrammes des valeurs d’expression avant et après cette sélection de gènes, et commentez les différences.

#### Histograms of expression before and after gene selection ####

par(mfrow = c(2,1))
hist(unlist(fa_expr_log2_standard), 
     breaks = seq(from = 0, to = max(fa_expr_log2_standard) + 1, by = 0.25),
     las = 1, 
     cex.axis = 0.8, 
     main = "Standardized values before gene selection",
     col =  "#DDBBFF")

hist(unlist(fa_expr_selected), 
     breaks = seq(from = 0, to = max(fa_expr_log2_standard) + 1, by = 0.25),
     las = 1, 
     cex.axis = 0.8, 
     main = "Standardized values after gene selection",
     col =  "#FFDDBB")
Distribution of expression values before and after gene selection =======

Sélection de gènes d’expression élevée et variable

Pour réduire le nombre de gènes, nous allons écarter les gènes faiblement exprimés (log2 moyen inférieur à 4), et ne retenir que ceux qui montrent des variations importantes entre échantillons. Pour ce dernier critère, nous nous basons sur le coefficient de variation afin de relativiser la dispersion (écart type) par rapport à la tendance centrale (moyenne).

Sélectionnez les gènes ayant un niveau log2 moyen minimal supérieur à 5 (\(m > 5\)) et une variance supérieure à 2 (\(s^2 > 2\)). Note: ces valeurs sont parfaitement arbitraires, elles ont été choisies pour obtenir un nombre raisonnable de gènes.

[1] "Selected genes: 874"

Dessinez des histogrammes des valeurs d’expression avant et après cette sélection de gènes, et commentez les différences.

Distribution of expression values before and after gene selection >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

Distribution of expression values before and after gene selection

<<<<<<< HEAD
par(mfrow = c(1,1))


# ## Some quick checks: the selection of highly variable genes select those having many zeros - and high values in other samples
# hist(unlist(fa_expr_log2_filtered[high_expression, ]), breaks=100)
# hist(unlist(fa_expr_log2_filtered[high_variation, ]), breaks=100)
# hist(unlist(fa_expr_log2_filtered[!high_variation, ]), breaks=100)
# hist(unlist(fa_expr_log2_filtered[selected_genes, ]), breaks=100)

Dessinez un box plot par échantillon des valeurs d’expression avant et après sélection des gènes, et commentez le résultat.

#### Histogram of expression after gene selection ####

par(mfrow = c(1,2))

boxplot(fa_expr_log2_standard, 
        horizontal = TRUE,
        xlab = "log2(counts)", 
        las = 1, 
        col = fa_meta$color, 
        main = "Before gene selection\nstandardised values")
boxplot(fa_expr_selected, 
        horizontal = TRUE,
        xlab = "log2(counts)", 
        las = 1, 
        col = fa_meta$color, 
        main = "After gene selection\nstandardised values")

ACP

Dessinez un plot ACP des échantillons en les colorant par condition avant et après normalisation.

  • avec les comptages bruts de la matrice d’expression initiale (\(fa_expr\))
<<<<<<< HEAD
## Raw expression values, all genes
ma_pca_raw_tt <- PCA(t(fa_expr_raw), 
                      scale.unit = FALSE, 
                      graph = FALSE)
# plot(ma_pca_raw_tt, choix = "ind")
fviz_pca_ind(ma_pca_raw_tt, col.ind = fa_meta[, "color"])
======= >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be
PC plot of the samples from the raw expression values of all genes.

PC plot of the samples from the raw expression values of all genes.

  • avec la matrice de valeurs filtrées et après transformation log2
<<<<<<< HEAD
ma_pca_filtered <- PCA(t(fa_expr_log2_filtered), scale.unit = FALSE,
                  graph = FALSE)
# plot(ma_pca_filtered, choix = "var")
# plot(ma_pca_sel, choix = "ind")
fviz_pca_ind(ma_pca_filtered, col.ind = fa_meta[, "color"])
PC plot of the samples from standardised values after gene selection.

PC plot of the samples from standardised values after gene selection. =======

PC plot of the samples from standardised values before gene selection.

PC plot of the samples from standardised values before gene selection. >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

  • avec la matrice finale (transformation log2, filtre des gènes non-détectés, standardisation et sélection des gènes fortement exprimés et à haut coefficient de variation)
<<<<<<< HEAD
ma_pca_sel <- PCA(t(fa_expr_selected), scale.unit = FALSE,
                  graph = FALSE)
# plot(ma_pca_sel, choix = "var")
# plot(ma_pca_sel, choix = "ind")
fviz_pca_ind(ma_pca_sel, col.ind = fa_meta[, "color"])
PC plot of the samples from standardised values after gene selection. =======
PC plot of the samples from standardised values after gene selection. >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

PC plot of the samples from standardised values after gene selection.

Clustering

  • Calculez les matrices de distance entre échantillons, en utilisant respectivement les distances euclidienne (dist()), coefficient de Pearson (cor(, method = "pearson")) et de Spearman (cor(, method = "spearman")).
<<<<<<< HEAD
#### Sample distances ####
dist_euc_sel <- dist(t(fa_expr_selected))
cor_pearson_sel <- as.dist(1 - cor(fa_expr_selected))
cor_spearman_sel <- 1 - as.dist(cor(fa_expr_selected, method = "spearman"))
  • Effectuez un clustering hiérarchique des échantillons, en utilisant le critère de Ward (ward.d2) pour l’agglomération. Comparez les arbres d’échantillons obtenus avec ces trois métriques et choisissez celle qui vous paraît la plus pertinente.
#### Sample clustering ####
par(mfrow = c(1,3))
plot(hclust(dist_euc_sel), hang = -1,
     main = "euclidean distance")
plot(hclust(cor_pearson_sel), hang = -1,
     main = "pearson")
plot(hclust(cor_spearman_sel), hang = -1,
     main = "spearman")
Sample tree with three alternative distance metrics: Euclidiant distance (left), Pearson correlation (center), Spearman correlation (right).? =======
  • Effectuez un clustering hiérarchique des échantillons, en utilisant le critère de Ward (ward.d2) pour l’agglomération. Comparez les arbres d’échantillons obtenus avec ces trois métriques et choisissez celle qui vous paraît la plus pertinente.
Sample tree with three alternative distance metrics: Euclidiant distance (left), Pearson correlation (center), Spearman correlation (right).? >>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

Sample tree with three alternative distance metrics: Euclidiant distance (left), Pearson correlation (center), Spearman correlation (right).?

<<<<<<< HEAD
par(mfrow = c(1,1))
  • Effectuez un clustering hiérarchique des gènes en utilisant la distance basée sur le coefficient de Pearson et le critère de Ward
cor_pearson_gene <- as.dist(1 - cor(t(fa_expr_selected)))
plot(hclust(cor_pearson_gene), hang = -1,
     main = "gènes")

  • Dessinez un arbre avec le résultat du clustering des gènes et commentez sa strcuture. Si vous deviez choisir de façon arbitraire un nombre de clusters, que choisiriez-vous ? Pourquoi ? Pas de panique, nous pouvons assumer ici que la réponse comporte une part de subjectivité.
plot(hclust(cor_pearson_gene), hang = -1,
     main = "gènes")
rect.hclust(hclust(cor_pearson_gene), k = 7)

  • Dessinez une heatmap du résultat, en sélectionnant les deux résultats de clustering ci-dessus pour les gènes et les échantillons.
pheatmap(t(fa_expr_selected),
         clustering_distance_cols = "correlation", clustering_distance_rows = "correlation")

=======
  • Effectuez un clustering hiérarchique des gènes en utilisant la distance basée sur le coefficient de Pearson et le critère de Ward

  • Dessinez un arbre avec le résultat du clustering des gènes et commentez sa strcuture. Si vous deviez choisir de façon arbitraire un nombre de clusters, que choisiriez-vous ? Pourquoi ? Pas de panique, nous pouvons assumer ici que la réponse comporte une part de subjectivité.

  • Dessinez une heatmap du résultat, en sélectionnant les deux résultats de clustering ci-dessus pour les gènes et les échantillons.

>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be

Interprétez les résultats en quelques phrases.

5. Enrichissement fonctionnel

Effectuez une analyse d’enrichissement fonctionnel avec les principaux clusters obtenus dans la section précédente.

[1] "result" "meta"  
 [1] "query"                 "significant"           "p_value"               "term_size"             "query_size"            "intersection_size"     "precision"             "recall"                "term_id"               "source"                "term_name"             "effective_domain_size"
[13] "source_order"          "parents"              
Length Class Mode
result 14 data.frame list
meta 5 -none- list
Top 10 most significant enriched functional classes
query significant p_value term_size query_size intersection_size precision recall term_id source term_name effective_domain_size source_order parents
58 query_1 TRUE 0.000000008144159 92 340 21 0.062 0.228 KEGG:04610 KEGG Complement and coagulation cascades 8787 299 KEGG:00000
2 query_1 TRUE 0.000000042399671 272 409 35 0.086 0.129 GO:0006955 GO:BP immune response 11240 2901 GO:0002376, GO:0050896
3 query_1 TRUE 0.000000042399671 331 409 39 0.095 0.118 GO:0002376 GO:BP immune system process 11240 1038 GO:0008150
25 query_1 TRUE 0.000000169692312 627 255 53 0.208 0.085 GO:0071944 GO:CC cell periphery 7218 3147 GO:0110165
26 query_1 TRUE 0.000003143387113 559 255 46 0.180 0.082 GO:0005886 GO:CC plasma membrane 7218 522 GO:0016020, GO:0071944
88 query_1 TRUE 0.000005079383120 114 357 21 0.059 0.184 REAC:R-MMU-381426 REAC Regulation of Insulin-like Growth Factor (IGF) transport and uptake by Insulin-like Growth Factor Binding Proteins (IGFBPs) 8631 1220 REAC:R-MMU-392499
89 query_1 TRUE 0.000005349656278 108 357 20 0.056 0.185 REAC:R-MMU-8957275 REAC Post-translational protein phosphorylation 8631 1054 REAC:R-MMU-597592
59 query_1 TRUE 0.000012274925197 116 340 19 0.056 0.164 KEGG:05150 KEGG Staphylococcus aureus infection 8787 416 KEGG:00000
27 query_1 TRUE 0.000019834136256 10 255 6 0.024 0.600 GO:0042613 GO:CC MHC class II protein complex 7218 2067 GO:0042611
90 query_1 TRUE 0.000019912758023 258 357 31 0.087 0.120 REAC:R-MMU-1474244 REAC Extracellular matrix organization 8631 452 REAC:0000000
[1] "query_metadata"  "result_metadata" "genes_metadata"  "timestamp"       "version"        

Conclusions générales

Résumez en quelques phrases vos conclusions à partir des résultats obtenus.

<<<<<<< HEAD
LS0tCnRpdGxlOiAiTWluaS1wcm9qZXQgMjAyMSAtIEV4cGxvcmF0aW9uIGRlcyBkb25uw6llcyBkZSBQYXZrb3ZpYyIKYXV0aG9yOiAiUHLDqW5vbSBOb20iCmRhdGU6ICdgciBTeXMuRGF0ZSgpYCcKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBzZWxmX2NvbnRhaW5lZDogeWVzCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogImhpZGUiCiAgcGRmX2RvY3VtZW50OgogICAgZmlnX2NhcHRpb246IHllcwogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KYGBge3Igc2V0dGluZ3MsIGluY2x1ZGU9RkFMU0UsIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0Kb3B0aW9ucyh3aWR0aCA9IDMwMCkKIyBvcHRpb25zKGVuY29kaW5nID0gJ1VURi04JykKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA1LCAKICBmaWcucGF0aCA9ICdmaWd1cmVzL21pbmktcHJvamV0XycsCiAgZmlnLmFsaWduID0gImNlbnRlciIsIAogIHNpemUgPSAidGlueSIsIAogIGVjaG8gPSBUUlVFLCAKICBldmFsID0gVFJVRSwgCiAgd2FybmluZyA9IEZBTFNFLCAKICBtZXNzYWdlID0gRkFMU0UsIAogIHJlc3VsdHMgPSBUUlVFLCAKICBjb21tZW50ID0gIiIpCgpvcHRpb25zKHNjaXBlbiA9IDEyKSAjIyBNYXggbnVtYmVyIG9mIGRpZ2l0cyBmb3Igbm9uLXNjaWVudGlmaWMgbm90YXRpb24KIyBrbml0cjo6YXNpc19vdXRwdXQoIlxcZm9vdG5vdGVzaXplIikKYGBgCgpgYGB7ciBsaWJyYXJpZXMsIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0KIyMjIyBSZXF1aXJlZCBsaWJyYXJpZXMgIyMjIwoKIyBMb2FkIHJlcXVpcmVkIENSQU4gUiBsaWJyYXJpZXMKcmVxdWlyZWRfY3JhbkxpYiA8LSBjKCJrbml0ciIsICJGYWN0b01pbmVSIiwgImZhY3RvZXh0cmEiLCAicGhlYXRtYXAiKQpmb3IgKGxpYiBpbiByZXF1aXJlZF9jcmFuTGliKSB7CiAgaWYgKCFyZXF1aXJlKGxpYiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhsaWIpCiAgfQogIHJlcXVpcmUobGliLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCn0KCnJlcXVpcmVkX2Jpb2NMaWIgPC0gYygiYmlvbWFSdCIpCmZvciAobGliIGluIHJlcXVpcmVkX2Jpb2NMaWIpIHsKICBpZiAoIXJlcXVpcmVOYW1lc3BhY2UobGliLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgICBCaW9jTWFuYWdlcjo6aW5zdGFsbChsaWIpCiAgfQogIHJlcXVpcmUobGliLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpCn0KCgprYWJsZShhcy5kYXRhLmZyYW1lKGMocmVxdWlyZWRfY3JhbkxpYiwgcmVxdWlyZWRfYmlvY0xpYikpLAogICAgICBjb2wubmFtZXMgPSAibGlicmFyaWVzIiwKICAgICAgY2FwdGlvbiA9ICJMb2FkZWQgcmVxdWlyZWQgbGlicmFyaWVzIgogICAgKQoKc2Vzc2lvbkluZm8oKQpgYGAKCiMjIFN5bm9wc2lzIGR1IHByb2pldAoKIyMjIFRyYXZhaWwgZGVtYW5kw6kKCkxlIGJ1dCBkZSBjZSB0cmF2YWlsIGVzdCBkZSBtZXR0cmUgZW4gb2V1dnJlIGxlcyBtw6l0aG9kZXMgdnVlcyBkYW5zIGxlIG1vZHVsZSAzICJSIGV0IHN0YXRpc3RpcXVlcyIgcG91ciBleHBsb3JlciBsZSBqZXUgZGUgZG9ubsOpZXMgZGUgUGF2b2tvdmljLCBldCBkZSByZW5kcmUgdW4gcmFwcG9ydCBkJ2FuYWx5c2UgYXUgZm9ybWF0IGAuUm1kYC4gCgpOb3VzIGZvdXJuaXNzb25zIGNpLWRlc3NvdXMgdW5lIHRyYW1lIGF2ZWMgbGVzIHByaW5jaXBhbGVzIHNlY3Rpb25zIGF0dGVuZHVlcy4gQ2VydGFpbmVzIGNvbnRpZW5uZW50IGTDqWrDoCBkdSBjb2RlLiBWb3VzIGRldnJleiBlbiBjb21wbMOpdGVyIGQnYXV0cmVzLiBTZW50ZXotdm91cyBsaWJyZXMgZCdhZGFwdGVyIGNldHRlIHRyYW1lIG91IGQneSBham91dGVyIGRlcyBhbmFseXNlcyBjb21wbMOpbWVudGFpcmVzIHNpIGVsbGVzIHZvdXMgYWlkZW50IMOgIGludGVycHLDqXRlciB2b3MgcsOpc3VsdGF0cy4gCgojIyMgUmVtaXNlIGR1IHJhcHBvcnQKCkRhdGU6ICoqbGUgMTAgbWFpIDIwMjEgbWludWl0KiouICBTaSB2b3VzIGFudGljaXBleiB1biBwcm9ibMOobWUgcG91ciByZW1ldHRyZSBsZSByYXBwb3J0IMOgIGNldHRlIGRhdGUgY29udGFjdGV6LW5vdXMgYXVzc2kgcmFwaWRlbWVudCBxdWUgcG9zc2libGUgcG91ciBxdWUgbm91cyBwdWlzc2lvbnMgcHLDqXZvaXIgdW5lIHJlbWlzZSBwbHVzIHRhcmRpdmUuIAoKLSBDb21tZW5jZXogcGFyIHJlbm9tbWVyIGxlIGZpY2hpZXIgLlJtZCBlbiByZW1wbGHDp2FudCBQcmVub20tTk9NIHBhciB2b3Mgbm9tIGV0IHByw6lub20uIAotIExlIHJhcHBvcnQgZXN0IGF0dGVuZHUgZW4gZm9ybWF0cyAuUm1kICsgLkhUTUwgKGVuIGdhcmRhbnQgbCdvcHRpb24gc2VsZl9jb250YWluZWQgZGUgbCdlbi10w6p0ZSBhY3RpdsOpZSkuIAotIETDqXBvc2V6IGxlcyBmaWNoaWVycyBkYW5zIHVuIHNvdXMtZG9zc2llciBkZSB2b3RlIGNvbXB0ZSBkdSBjbHVzdGVyLiBBdHRlbnRpb24sIHZlaWxsZXogw6AgcmVzcGVjdGVyIHByw6ljaXPDqW1lbnQgY2V0dGUgc3RydWN0dXJlIGRlIGNoZW1pbiBjYXIgbm91cyBub3VzIGJhc2Vyb25zIGRlc3N1cyBwb3VyIHLDqWN1cMOpcmVyIHZvcyByw6lzdWx0YXRzLiAKCiAgICBgL3NoYXJlZC9wcm9qZWN0cy9kdWJpaTIwMjEvW2xvZ2luXS9tMy1zdGF0LVIvbWluaS1wcm9qZXRgIAoKIyMjIENyaXTDqHJlcyBkJ8OpdmFsdWF0aW9uCgotIFJlcHJvZHVjdGliaWxpdMOpIGRlcyBhbmFseXNlczogbm91cyB0ZW50ZXJvbnMgZGUgcmVnw6luw6lyZXIgbGUgcmFwcG9ydCBIVE1MIMOgIHBhcnRpciBkZSB2b3RyZSBSbWQsIGVuIHBhcnRhbnQgZGUgbm90cmUgY29tcHRlIHN1ciBsZSBzZXJ2ZXVyIElGQi4gCi0gTWFuaXB1bGF0aW9uIGRlcyBvYmpldHMgUgotIE1vYmlsaXNhdGlvbiBkZXMgbcOpdGhvZGVzIHN0YXRpc3RpcXVlcyB2dWVzIGF1IGNvdXJzCi0gUGVydGluZW5jZSBkZXMgaW50ZXJwcsOpdGF0aW9ucyBzdGF0aXN0aXF1ZXMKLSBQZXJ0aW5lbmNlIGRlcyBpbnRlcnByw6l0YXRpb25zIGJpb2xvZ2lxdWVzCi0gQ2xhcnTDqSBkZSBsYSByw6lkYWN0aW9uCi0gQ2xhcnTDqSBkZXMgaWxsdXN0cmF0aW9ucyAoZmlndXJlcyBldCB0YWJsZWF1eCk6IGdyYXBoaXNtZXMsIGzDqWdlbmRlcyAuLi4KCk5vdXMgdm91cyBlbmNvdXJhZ2VvbnMgw6AgYXNzdXJlciBsYSBsaXNpYmlsaXTDqSBkZSB2b3RyZSBjb2RlIChzeW50YXhlLCBub21tYWdlIGRlcyB2YXJpYWJsZXMsIGNvbW1lbnRhaXJlcyBkZSBjb2RlKQoKIyMjIE9iamVjdGlmcyBzY2llbnRpZmlxdWVzCgpOb3VzIHBhcnRvbnMgZHUgbcOqbWUgamV1IGRlIGRvbm7DqWVzICpGaWwgUm91Z2UqIGRlIGNlIG1vZHVsZSBpc3N1ZXMgZGUgbGEgcHVibGljYXRpb24gUGF2a292aWMsIE0uLCBQYW50YW5vLCBMLiwgR2VybGFjaCwgQy5WLiBldCBhbC4gTXVsdGkgb21pY3MgYW5hbHlzaXMgb2YgZmlicm90aWMga2lkbmV5cyBpbiB0d28gbW91c2UgbW9kZWxzLiBTY2kgRGF0YSA2LCA5MiAoMjAxOSkuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L3M0MTU5Ny0wMTktMDA5NS01CgoqKlJhcHBlbCBzdXIgbGVzIMOpY2hhbnRpbGxvbnM6KioKCkRldXggbW9kw6hsZXMgZGUgZmlicm9zZSByw6luYWxlIGNoZXogbGEgc291cmlzIHNvbnQgw6l0dWRpw6lzOgoKMS4gTGUgcHJlbWllciBlc3QgdW4gbW9kw6hsZSBkZSBuw6lwaHJvcGF0aGllIHLDqXZlcnNpYmxlIGluZHVpdGUgcGFyIGwnYWNpZGUgZm9saXF1ZSAoZm9saWMgYWNpZCAoRkEpKS4gTGVzIHNvdXJpcyBvbnQgw6l0w6kgc2FjcmlmacOpZXMgYXZhbnQgbGUgdHJhaXRlbWVudCAobm9ybWFsKSwgcHVpcyDDoCBqb3VyIDEsIDIsIDcgZXQgMTQgKGRheTEsLi4uKSBhcHLDqHMgdW5lIHNldWxlIGluamVjdGlvbiBkJ2FjaWRlIGZvbGlxdWUuCgoyLiBMZSBzZWNvbmQgZXN0IHVuIG1vZMOobGUgaXJyw6l2ZXJzaWJsZSBpbmR1aXQgY2hyaXJ1cmdpY2FsZW1lbnQgKHVuaWxhdGVyYWwgdXJldGVyYWwgb2JzdHJ1Y3Rpb24gKFVVTykpLiBsZXMgc291cmlzIG9udCDDqXTDqSBzYWNyaWZpw6llcyBhdmFudCBvYnN0cnVjdGlvbiAoZGF5IDApIGV0IMOgIDMsIDcgZXQgMTQgam91cnMgYXByw6hzIG9ic3RydWN0aW9uIHBhciBsaWdhdGlvbiBkZSBsJ3VyZXTDqHJlIGR1IHJlaW4gZ2F1Y2hlLgoKQSBwYXJ0aXIgZGUgY2VzIGV4dHJhaXRzIGRlIHJlaW4sIGwnQVJOIG1lc3NhZ2VyIHRvdGFsIGV0IGxlcyBwZXRpdHMgQVJOcyBvbnQgw6l0w6kgc8OpcXVlbmPDqXMgZXQgbGVzIHByb3TDqWluZXMgY2FyYXTDqXJpc8OpZXMgcGFyIHNwZWN0cm9tw6l0cmllIGRlIG1hc3NlIGVuIHRhbmRlbSAoVE1UKS4KCioqQnV0IHNjaWVudGlmaXF1ZToqKiBEYW5zIGxlIHR1dG9yaWVsIHN1ciBsZXMgZGF0YWZyYW1lcywgdm91cyBhdmV6IHRyYXZhaWxsw6kgc3VyIGxlcyBkb25uw6llcyBkZSAqKip0cmFuc2NyaXB0b21lIGR1IG1vZMOobGUgVVVPKioqLiBEYW5zIGNlIG1pbmktcHJvamV0LCB2b3VzIGFsbGV6IHRyYXZhaWxsZXIgc3VyIGxlcyBkb25uw6llcyBkdSAqKip0cmFuc2NyaXB0b21lIGR1IG1vZMOobGUgRkEqKiogYWZpbiBkZSByZWdyb3VwZXIgbGVzIG9ic2VydmF0aW9ucyAow6ljaGFudGlsbG9uKSBldCBsZXMgZ8OobmVzIHNlbG9uIGRlcyBwcm9maWxzIGQnZXhwcmVzc2lvbiBzaW1pbGFpcmVzLgoKKipWb3RyZSBwcm9qZXQgc2UgZMOpY29tcG9zZSBlbiA0IHBhcnRpZXM6KioKCjEuIHN0YXRzaXRpcXVlcyBkZXNjcmlwdGl2ZXMgZGVzIGRvbm7DqWVzIGJydXRlczogY29tbWFuZGVzIGZvdXJuaWVzCjIuIG5vcm1hbGlzYXRpb24gZGVzIGRvbm7DqWVzIDogY29tbWFuZGVzIGZvdXJuaWVzCjMuIHN0YXRpc3RpcXVlcyBkZXNjcmlwdGl2ZXMgZGVzIGRvbm7DqWVzIG5vcm1hbGlzw6llczogw6Agdm91cyBkZSBqb3Vlcgo0LiBhbmFseXNlIGRlIHJlZ3JvdXBlbWVudCBkZXMgZG9ubsOpZXM6IMOgIHZvdXMgZGUgam91ZXIKCiMjIDEuIExlcyBkb25uw6llcyBicnV0ZXMKCioqKlZvdXMgbidhdmV6IHJpZW4gw6AgY29kZXIgaWNpLiBMZSBjb2RlIGVzdCBmb3VybmkuKioqCgojIyMgQ2hhcmdlbWVudCBkZXMgZG9ubsOpZXMgYnJ1dGVzCgpMZSBibG9jIHN1aXZhbnQgY29udGllbnQgdW5lIGZvbmN0aW9uIHF1aSBwZXJtZXQgZGUgdMOpbMOpY2hhcmdlciB1biBmaWNoaWVyIGRhbnMgbCdlc3BhY2UgZGUgdHJhdmFpbCwgc2F1ZiBzJ2lsIGVzdCBkw6lqw6AgcHLDqXNlbnQuIE5vdXMgbCd1dGlsaXNlcm9ucyBlbnN1aXRlIHBvdXIgdMOpbMOpY2hhcmdlciBsZXMgZG9ubsOpZXMgw6AgYW5hbHlzZXIgZW4gw6l2aXRhbnQgZGUgcmVmYWlyZSBsZSB0cmFuc2ZlcnQgw6AgY2hhcXVlIGV4w6ljdXRpb24gZGUgbCdhbmFseXNlLiAKCmBgYHtyIGZ1bmN0aW9uX2Rvd25sb2FkX29ubHlfb25jZX0KIycgQHRpdGxlIERvd25sb2FkIGEgZmlsZSBvbmx5IGlmIGl0IGlzIG5vdCB5ZXQgaGVyZQojJyBAYXV0aG9yIEphY3F1ZXMgdmFuIEhlbGRlbiBlbWFpbHtKYWNxdWVzLnZhbi1IZWxkZW5AQGZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnJ9CiMnIEBwYXJhbSB1cmxfYmFzZSBiYXNlIG9mIHRoZSBVUkwsIHRoYXQgd2lsbCBiZSBwcmVwZW5kZWQgdG8gdGhlIGZpbGUgbmFtZQojJyBAcGFyYW0gZmlsZV9uYW1lIG5hbWUgb2YgdGhlIGZpbGUgKHNob3VsZCBub3QgY29udGFpbiBhbnkgcGF0aCkKIycgQHBhcmFtIGxvY2FsX2ZvbGRlciBwYXRoIG9mIGEgbG9jYWwgZm9sZGVyIHdoZXJlIHRoZSBmaWxlIHNob3VsZCBiZSBzdG9yZWQKIycgQHJldHVybiB0aGUgZnVuY3Rpb24gcmV0dXJucyB0aGUgcGF0aCBvZiB0aGUgbG9jYWwgZmlsZSwgYnVpbHQgZnJvbSBsb2NhbF9mb2xkZXIgYW5kIGZpbGVfbmFtZQojJyBAZXhwb3J0wqkKZG93bmxvYWRfb25seV9vbmNlIDwtIGZ1bmN0aW9uKAogIHVybF9iYXNlLCAKICBmaWxlX25hbWUsCiAgbG9jYWxfZm9sZGVyKSB7CgogICMjIERlZmluZSB0aGUgc291cmNlIFVSTCAgCiAgdXJsIDwtIGZpbGUucGF0aCh1cmxfYmFzZSwgZmlsZV9uYW1lKQogIG1lc3NhZ2UoIlNvdXJjZSBVUkxcblx0IiwgIHVybCkKCiAgIyMgRGVmaW5lIHRoZSBsb2NhbCBmaWxlCiAgbG9jYWxfZmlsZSA8LSBmaWxlLnBhdGgobG9jYWxfZm9sZGVyLCBmaWxlX25hbWUpCiAgCiAgIyMgQ3JlYXRlIHRoZSBsb2NhbCBkYXRhIGZvbGRlciBpZiBpdCBkb2VzIG5vdCBleGlzdAogIGRpci5jcmVhdGUobG9jYWxfZm9sZGVyLCBzaG93V2FybmluZ3MgPSBGQUxTRSwgcmVjdXJzaXZlID0gVFJVRSkKICAKICAjIyBEb3dubG9hZCB0aGUgZmlsZSBPTkxZIGlmIGl0IGlzIG5vdCBhbHJlYWR5IHRoZXJlCiAgaWYgKCFmaWxlLmV4aXN0cyhsb2NhbF9maWxlKSkgewogICAgbWVzc2FnZSgiRG93bmxvYWRpbmcgZmlsZSBmcm9tIHNvdXJjZSBVUkwgdG8gbG9jYWwgZmlsZVxuXHQiLCAKICAgICAgICAgICAgbG9jYWxfZmlsZSkKICAgIGRvd25sb2FkLmZpbGUodXJsID0gdXJsLCBkZXN0ZmlsZSA9IGxvY2FsX2ZpbGUpCiAgfSBlbHNlIHsKICAgIG1lc3NhZ2UoIkxvY2FsIGZpbGUgYWxyZWFkeSBleGlzdHMsIG5vIG5lZWQgdG8gZG93bmxvYWRcblx0IiwgCiAgICAgICAgICAgIGxvY2FsX2ZpbGUpCiAgfQogIAogIHJldHVybihsb2NhbF9maWxlKQp9CmBgYAoKTm91cyB0w6lsw6ljaGFyZ2VvbnMgZGV1eCBmaWNoaWVycyBkYW5zIHVuIGRvc3NpZXIgbG9jYWwgYH4vbTMtc3RhdC1SL3BhdmtvdmljX2FuYWx5c2lzYCAqKih2b3VzIHBvdXZleiBjaGFuZ2VyIGxlIG5vbSBvdSBjaGVtaW4gZGFucyBsZSBjaHVuayBjaS1kZXNzb3VzKSoqLCBldCBsZXMgY2hhcmdlb25zIGRhbnMgbGVzIGRhdGEuZnJhbWVzIHN1aXZhbnRzOiAKCi0gRG9ubsOpZXMgYnJ1dGVzIGRlIHRyYW5zY3JpcHRvbWU6IGBmYV9leHByX3Jhd2AKLSBNw6l0YWRvbm7DqWVzOiBgZmFfbWV0YWAKCmBgYHtyIGRvd25sb2FkX2FuZF9sb2FkfQojIyBEZWZpbmUgdGhlIHJlbW90ZSBVUkwgYW5kIGxvY2FsIGZvbGRlcgpwYXZrb3ZpY191cmwgPC0gImh0dHBzOi8vZ2l0aHViLmNvbS9EVS1CaWkvbW9kdWxlLTMtU3RhdC1SL3Jhdy9tYXN0ZXIvc3RhdC1SXzIwMjEvZGF0YS9wYXZrb3ZpY18yMDE5LyIKCiMjIERlZmluZSB0aGUgbG9jYWwgZm9sZGVyIGZvciB0aGlzIGFuYWx5c2lzICh3aGVyZSB0aGUgZGF0YSB3aWxsIGJlIGRvd25sb2FkZWQgYW5kIHRoZSByZXN1bHRzIGdlbmVyYXRlZCkKcGF2a292aWNfZm9sZGVyIDwtICJ+L20zLXN0YXQtUi9wYXZrb3ZpY19hbmFseXNpcyIKCiMjIERlZmluZSBhIHN1Yi1mb2xkZXIgZm9yIHRoZSBkYXRhCnBhdmtvdmljX2RhdGFfZm9sZGVyIDwtIGZpbGUucGF0aChwYXZrb3ZpY19mb2xkZXIsICJkYXRhIikKCiMjIERvd25sb2FkIGFuZCBsb2FkIHRoZSBleHByZXNzaW9uIGRhdGEgdGFibGUKIyMgTm90ZTogd2UgdXNlIGNoZWNrLm5hbWVzPUZBTFNFIHRvIGF2b2lkIHJlcGxhY2luZyBoeXBoZW5zIGJ5IGRvdHMKIyMgaW4gc2FtcGxlIG5hbWVzLCBiZWNhdXNlIHdlIHdhbnQgdG8ga2VlcCB0aGVtIGFzIGluIHRoZSAKIyMgb3JpZ2luYWwgZGF0YSBmaWxlcy4gCm1lc3NhZ2UoIkRvd25sb2FkaW5nIEZBIHRyYW5zY3JpcHRvbWUgZmlsZVx0IiwgImZhX3Jhd19jb3VudHMudHN2Lmd6IiwKICAiXG5cdGZyb21cdCIsIHBhdmtvdmljX3VybCkKZmFfZXhwcl9maWxlIDwtIGRvd25sb2FkX29ubHlfb25jZSgKICB1cmxfYmFzZSA9IHBhdmtvdmljX3VybCwgCiAgZmlsZV9uYW1lID0gImZhX3Jhd19jb3VudHMudHN2Lmd6IiwKICBsb2NhbF9mb2xkZXIgPSBwYXZrb3ZpY19kYXRhX2ZvbGRlcikKCiMjIExvYWQgdGhlIGV4cHJlc2RzaW9uIHRhYmxlCm1lc3NhZ2UoIkxvYWRpbmcgRkEgdHJhbnNjcmlwdG9tZSBkYXRhIGZyb21cblx0IiwgZmFfZXhwcl9maWxlKQpmYV9leHByX3JhdyA8LSByZWFkLmRlbGltKGZpbGUgPSBmYV9leHByX2ZpbGUsIAogICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEpCgojIyBEb3dubG9hZCB0aGUgbWV0YWRhdGEgZmlsZQptZXNzYWdlKCJEb3dubG9hZGluZyBGQSBtZXRhZGF0YSBmaWxlXHQiLCAiZmFfdHJhbnNjcmlwdG9tZV9tZXRhZGF0YS50c3YiLAogICJcblx0ZnJvbVx0IiwgcGF2a292aWNfdXJsKQpmYV9tZXRhX2ZpbGUgPC0gZG93bmxvYWRfb25seV9vbmNlKAogIHVybF9iYXNlID0gcGF2a292aWNfdXJsLCAKICBmaWxlX25hbWUgPSAiZmFfdHJhbnNjcmlwdG9tZV9tZXRhZGF0YS50c3YiLAogIGxvY2FsX2ZvbGRlciA9IHBhdmtvdmljX2RhdGFfZm9sZGVyKQoKIyMgTG9hZCB0aGUgbWV0YWRhdGEKbWVzc2FnZSgiTG9hZGluZyBGQSBtZXRhZGF0YSBmcm9tXG5cdCIsIGZhX21ldGFfZmlsZSkKZmFfbWV0YSA8LSByZWFkLmRlbGltKGZpbGUgPSBmYV9tZXRhX2ZpbGUsIAogICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IDEpCmBgYAoKTm91cyByZWdhcmRvbnMgbGEgc3RydWN0dXJlIGRlIGNoYXF1ZSBkYXRhZnJhbWUuCgpgYGB7ciBpbnNlcGN0IGRhdGF9CnN0cihmYV9leHByX3JhdykKc3RyKGZhX21ldGEpCmBgYAoKTGVzIGRldXggZmljaGllcnMgbmUgZG9ubmVudCBwYXMgbGVzIG9ic2VydmF0aW9ucyBkZSBsJ8OpY2hhbnRpbGxvbiBkYW5zIGxlIG3Dqm1lIG9yZHJlOgoKYGBge3IgY2hlY2sgZGF0YSBvcmRlcn0KZmFfbWV0YSRzYW1wbGVOYW1lID09IG5hbWVzKGZhX2V4cHJfcmF3KQpgYGAKCk5vdXMgbGVzIHLDqW9yZ2FuaXNvbnMgbGVzIMOpY2hhbnRpbGxvbnMgZGFucyBsJ29yZHJlIGRlIGwnZXhww6lyaWVuY2U6IGNvbmRpdGlvbiBub3JtYWxlLCBwdWlzIGRheSAxIMOgIDE0IGF2ZWMgbGVzIDMgcsOpcGxpY2F0cy4KCmBgYHtyIHJlb2RlciBkYXRhfQpzYW1wbGVfb3JkZXIgPC0gYyhwYXN0ZShyZXAoYygibm9ybWFsIiwgImRheTEiLCAiZGF5MiIsICJkYXkzIiwgImRheTciLCAiZGF5MTQiKSwgZWFjaCA9IDMpLAogICAgICAgICAgICAgICAgICAgICAgICAxOjMsIHNlcCA9ICJfIikpCgpmYV9leHByX3JhdyA8LSBmYV9leHByX3Jhd1ssc2FtcGxlX29yZGVyXQpmYV9tZXRhIDwtIGZhX21ldGFbbWF0Y2goc2FtcGxlX29yZGVyLCBmYV9tZXRhJHNhbXBsZU5hbWUpLF0KCiMgVmlldyhmYV9tZXRhKQprYWJsZShmYV9tZXRhLCBjYXB0aW9uID0gIk1ldGRhdGEgZm9yIFBhdmtvdm9jIEZBIHRyYW5zY3JpcHRvbWUiKQpgYGAKCj0+IEFpbnNpLCBub3VzIGF2b25zIHVuIGpldSBkZSBkb25uw6llcyBhdmVjIHVuIMOpY2hhbnRpbGxvbiBkZSBgciBucm93KGZhX21ldGEpYCBvYnNlcnZhdGlvbnMgZXQgZGVzIGRvbm7DqWVzIGQnZXhwcmVzc2lvbiBkZSBgciBucm93KGZhX2V4cHJfcmF3KWAgZ8OobmVzLgoKIyMjIFRyYW5zZm9ybWF0aW9uIGxvZzIKCkFwcGxpcXVleiB1bmUgdHJhbnNmb3JtYXRpb24gbG9nMiBkZXMgZG9ubsOpZXMgYnJ1dGVzLCBhcHLDqHMgYXZvaXIgYWpvdXTDqSB1biBlcHNpbG9uICRcZXBzaWxvbiA9IDEkIChsZXMgdmFsZXVycyBudWxsZXMgc2Vyb250IGRvbmMgcmVwcsOpc2VudMOpZXMgcGFyIHVuIGxvZzIoY291bnRzKSB2YWxhbnQgJDAkLiBTdG9ja2V6IGxlIHLDqXN1bHRhdCBkYW5zIHVuIGRhdGEuZnJhbWUgbm9tbcOpIGBmYV9leHByX2xvZzJgLgoKQWZmY2hleiB1biBmcmFnbWVudCBkZXMgdGFibGVhdXggYGZhX2V4cHJfcmF3YCBldCBgZmFfZXhwcl9sb2cyYCBlbiBzw6lsZWN0aW9ubmFudCBsZXMgbGlnbmVzIDEwMCDDoCAxMDkgZXQgbGVzIGNvbG9ubmVzIDUgw6AgMTAsIGFmaW4gZGUgdm91cyBhc3N1cmVyIHF1ZSBsYSB0cmFuc2Zvcm1hdGlvbiBsb2cyIGEgYmllbiBmb25jdGlvbm7DqS4gCgpgYGB7ciBsb2cyX3RyYW5zZm9ybX0KIyMgTG9nMiB0cmFuc2Zvcm1hdGlvbiBvZiB0aGUgdHJhbnNjcmlwdG9tZSBkYXRhCmVwc2lsb24gPC0gMQpmYV9leHByX2xvZzIgPC0gbG9nMihmYV9leHByX3JhdyArIGVwc2lsb24pCiMgZGltKGZhX2V4cHJfbG9nMikKIyBWaWV3KGhlYWQoZmFfZXhwcl9sb2cyKSkKCiMjIERpc3BsYXkgb2YgYSBmcmFnbWVudCBvZiB0aGUgZGF0YSBiZWZvcmUgYW5kIGFmdGVyIGxvZzIgdHJhbnNmb3JtYXRpb24Ka2FibGUoZmFfZXhwcl9yYXdbMTAwOjEwOSwgNToxMF0sIGNhcHRpb24gPSAiRnJhZ21lbnQgZGVzIGRvbm7DqWVzIHRyYW5zY3JpcHRvbWlxdWVzIGJydXRlcyIpCmthYmxlKGZhX2V4cHJfbG9nMlsxMDA6MTA5LCA1OjEwXSwgY2FwdGlvbiA9ICJGcmFnbWVudCBkZXMgZG9ubsOpZXMgdHJhbnNjcmlwdG9taXF1ZXMgYXByw6hzIHRyYW5zZm9ybWF0aW9uIGxvZzIiKQpgYGAKCiMjIyBTdGF0aXN0aXF1ZXMgZGVzY3JpcHRpdmVzCgpEYW5zIGxlIHR1dG9yaWFsIHN1ciBsZXMgZGF0YWZyYW1lcyBzdXIgbGUgamV1IGRlIGRvbm7DqWVzICJ1dW8iIChyZWxpc2V6IGxlIGNvcnJpZ8OpKSwgbm91cyB2b3VzIGF2b25zIGRlbWFuZMOpIGRlIGNyw6llciB1biBkYXRhLmZyYW1lIHF1aSBjb2xsZWN0ZXJhIGxlcyBzdGF0aXN0aXF1ZXMgcGFyIGfDqG5lIGV0IHBhciDDqWNoYW50aWxsb24uIE5vdXMgdm91cyBkZW1hbmRvbnMgZGUgcsOpYWxpc2VyIHVuZSDDqXR1ZGUgc2ltaWxhaXJlIHN1ciBsZXMgZG9ubsOpZXMgIkZBIi4KCiMjIyMgUGFyIMOpY2hhbnRpbGxvbiBhdmFudCBub3JtYWxpc2F0aW9uCgpOb3VzIGNyw6lvbnMgdW4gZGF0YS5mcmFtZSBub21tw6kgYHNhbXBsZV9zdGF0X3ByZW5vcm1gIHF1aSBjb21wb3J0ZXJhIHVuZSBsaWduZSBwYXIgw6ljaGFudGlsbG9uIGV0IHVuZSBjb2xvbm5lIHBhciBzdGF0aXN0aXF1ZS4gTm91cyBjYWxjdWxvbnMgbGVzIHN0YXRpc3RpcXVlcyBzdWl2YW50ZXMgc3VyIGxlcyB2YWxldXJzIGxvZzIgZCdleHByZXNzaW9uIGRlIGNoYXF1ZSDDqWNoYW50aWxsb246CgotIG1veWVubmUKLSDDqWNhcnQtdHlwZQotIGludGVydmFsbGUgaW50ZXItcXVhcnRpbGVzCi0gcHJlbWllciBxdWFydGlsZQotIG3DqWRpYW5lCi0gdHJvaXNpw6htZSBxdWFydGlsZQotIG1heGltdW0KLSBub21icmUgZGUgdmFsZXVycyBudWxsZXMKCklsIHNlcmEgYWZmaWNow6kgYXZlYyBsYSBmb25jdGlvbiBga2FibGUoKWAgKG4nb3VibGlleiBwYXMgbGEgbMOpZ2VuZGUpLiAKCmBgYHtyIHNhbXBsZV9zdGF0X3ByZV9ub3JtfQoKc2FtcGxlX3N0YXRfcHJlbm9ybSA8LSBkYXRhLmZyYW1lKAogIG1lYW4gPSBhcHBseShmYV9leHByX2xvZzIsIDIsIG1lYW4sIG5hLnJtPVRSVUUpLAogIHNkID0gYXBwbHkoZmFfZXhwcl9sb2cyLCAyLCBzZCwgbmEucm09VFJVRSksCiAgaXFyID0gYXBwbHkoZmFfZXhwcl9sb2cyLCAyLCBJUVIsIG5hLnJtPVRSVUUpLAogIFExID0gYXBwbHkoZmFfZXhwcl9sb2cyLCAyLCBxdWFudGlsZSwgcCA9IDAuMjUsIG5hLnJtPVRSVUUpLAogIG1lZGlhbiA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMiwgbWVkaWFuLCBuYS5ybT1UUlVFKSwKICBRMyA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMiwgcXVhbnRpbGUsIHAgPSAwLjc1LCBuYS5ybT1UUlVFKSwKICBtYXggPSBhcHBseShmYV9leHByX2xvZzIsIDIsIG1heCwgbmEucm09VFJVRSksCiAgbnVsbCA9IGFwcGx5KGZhX2V4cHJfbG9nMiA9PSAwLCAyLCBzdW0sIG5hLnJtPVRSVUUpCikKCmthYmxlKHNhbXBsZV9zdGF0X3ByZW5vcm0sIGNhcHRpb24gPSAiU2FtcGxlLXdpc2Ugc3RhdGlzdGljcyBiZWZvcmUgbm9ybWFsaXNhdGlvbi4iKQoKYGBgCgojIyMjIFBhciBnw6huZSBhdmFudCBub3JtYWxpc2F0aW9uCgpOb3VzIGNyw6lvbnMgY2ktZGVzc291cyB1biBkYXRhLmZyYW1lIG5vbW3DqSBgZ2VuZV9zdGF0X3ByZW5vcm1gIHF1aSBjb21wb3J0ZXJhIHVuZSBsaWduZSBwYXIgZ8OobmUgZXQgdW5lIGNvbG9ubmUgcGFyIHN0YXRpc3RpcXVlLiBOb3VzIGNhbGN1bG9ucyBsZXMgc3RhdGlzdGlxdWVzIHN1aXZhbnRlcyBzdXIgbGVzIHZhbGV1cnMgbG9nMiBkZSBjaGFxdWUgZ8OobmUuCgotIG1veWVubmUKLSBtw6lkaWFuZQotIMOpY2FydC10eXBlCi0gcHJlbWllciBxdWFydGlsZQotIHRyb2lzacOobWUgcXVhcnRpbGUKLSBtYXhpbXVtCi0gbm9tYnJlIGRlIHZhbGV1cnMgbnVsbGVzCi0gaW50ZXJ2YWxsZSBpbnRlci1xdWFydGlsZXMKCkNlcyByw6lzdWx0YXRzIHNlcm9udCBzdG9ja8OpcyBkYW5zIHVuIGRhdGEuZnJhbWUgYXZlYyAxIGxpZ25lIHBhciDDqWNoYW50aWxsb24gZXQgMSBjb2xvbm5lIHBhciBzdGF0aXN0aXF1ZS4gVm91cyBhZmZpY2hlcmV6IGxlcyBsaWduZXMgMTAwIMOgIDEwOSBkZSBjZSB0YWJsZWF1IGRlIHN0YXRpc3RpcXVlcyBhdmVjIGxhIGZvbmN0aW9uIGBrYWJsZSgpYCAobidvdWJsaWV6IHBhcyBsYSBsw6lnZW5kZSkuCgpgYGB7ciBnZW5lX3N0YXRfcHJlX25vcm19CiMjIEdlbmUtd2lzZSBzdGF0aXN0aWNzIGJlZm9yZSBub3JtYWxpc2F0aW9uCmdlbmVfc3RhdF9wcmVub3JtIDwtIGRhdGEuZnJhbWUoCiAgbWVhbiA9IGFwcGx5KGZhX2V4cHJfcmF3LCAxLCBtZWFuLCBuYS5ybSA9IFRSVUUpLAogIHNkID0gYXBwbHkoZmFfZXhwcl9yYXcsIDEsIHNkLCBuYS5ybSA9IFRSVUUpLAogIGlxciA9IGFwcGx5KGZhX2V4cHJfcmF3LCAxLCBJUVIsIG5hLnJtID0gVFJVRSksCiAgUTEgPSBhcHBseShmYV9leHByX3JhdywgMSwgcXVhbnRpbGUsIHAgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogIG1lZGlhbiA9IGFwcGx5KGZhX2V4cHJfcmF3LCAxLCBtZWRpYW4sIG5hLnJtID0gVFJVRSksCiAgUTMgPSBhcHBseShmYV9leHByX3JhdywgMSwgcXVhbnRpbGUsIHAgPSAwLjc1LCBuYS5ybSA9IFRSVUUpLAogIG1heCA9IGFwcGx5KGZhX2V4cHJfcmF3LCAxLCBtYXgsIG5hLnJtID0gVFJVRSksCiAgbnVsbCA9IGFwcGx5KGZhX2V4cHJfcmF3ID09IDAsIDEsIHN1bSwgbmEucm0gPSBUUlVFKQopCgprYWJsZShnZW5lX3N0YXRfcHJlbm9ybVsxMDA6MTA5LCBdLCBjYXB0aW9uID0gIkdlbmUtd2lzZSBzdGF0aXN0aWNzIGJlZm9yZSBub3JtYWxpc2F0aW9uIikKCmBgYAoKIyMgMi4gRmlsdHJhZ2UgZXQgbm9ybWFsaXNhdGlvbiBkZXMgZG9ubsOpZXMKCioqKlZvdXMgbidhdmV6IHJpZW4gw6AgY29kZXIgaWNpLiBMZSBjb2RlIGVzdCBmb3VybmkuKioqCgpJbCBleGlzdGUgcGx1c2lldXJzIGZhw6dvbnMgZGUgbm9ybWFsaXNlciBsZXMgZG9ubsOpZXMgZGUgdHJhbnNjcmlwdG9tZSAgdnVlcyBkYW5zIGxlcyBtb2R1bGVzIDQgZXQgNSAoY2YuIHRvdGFsIGNvdW50cywgcXVhbnRpbGVzLCBUTU0sIFJMRSwgbGltbWEgdm9vbSwuLi4pLCBtYWlzIG5vdXMgYXZvbnMgY2hvaXNpIGljaSB1bmUgc29sdXRpb24gc2ltcGxlIHRvdXQgZW4gw6l0YW50IHJvYnVzdGUgcG91ciBub3JtYWxpc2VyIGxlcyBkb25uw6llcyBlbiBzdGFuZGFyZGlzYW50IGxlIDPDqG1lIHF1YW50aWxlLiAKCkxhIG3DqXRob2RlIGNob2lzaWUgaWNpIGNvbnNpc3RlIMOgIDoKCi0gw6ljYXJ0ZXIgbGVzIGfDqG5lcyAibm9uLWTDqXRlY3TDqXMiLCBjJ2VzdC3DoC1kaXJlIGNldXggYXlhbnQgZGVzIHZhbGV1cnMgbnVsbGVzIGRhbnMgYXUgbW9pbnMgOTAlIGRlcyDDqWNoYW50aWxsb25zOwoKLSDDqWNhcnRlciBsZXMgZ8OobmVzIMOgIHBlaW5lIGV4cHJpbcOpcywgYydlc3Qtw6AtZGlyZSBjZXV4IGF5YW50IHVuZSB2YWxldXIgbW95ZW5uZSA8IDEwIChhcmJpdHJhaXJlbWVudCk7CgotIHN0YW5kYXJkaXNlciBsZXMgw6ljaGFudGlsbG9ucyBzdXIgbGUgM8OobWUgcXVhcnRpbGUgZGVzIHZhbGV1cnMgbm9uLW51bGxlczogb24gZGl2aXNlIHBhciBsZSAzw6htZSBxdWFydGlsZSBkZSBsJ8OpY2hhbnRpbGxvbiBldCBvbiBtdWx0aXBsaWUgcGFyIGxlIDPDqG1lIHF1YXJ0aWxlIGTDqXRlcm1pbsOpIHN1ciBsJ2Vuc2VtYmxlIGRlcyDDqWNoYW50aWxsb25zLgoKTm91cyBmb3Vybmlzc29ucyBjaS1kZXNzb3VzIGxlIGNvZGUuCgojIyMgRmlsdHJhZ2UgMSA6IMOpbGltaW5hdGlvbiBkZXMgZ8OobmVzIG5vbiBkw6l0ZWN0w6lzIG91IMOgIHBlaW5lIGV4cHJpbcOpcwoKYGBge3IgZ2VuZV9maWx0ZXJpbmd9CiMjIERhdGEgZmlsdGVyaW5nOiBnZW5lcyBoYXZpbmcgYXQgbGVhc3QgOTAlIG51bGwgdmFsdWVzCnVuZGV0ZWN0ZWRfZ2VuZXMgPC0gZ2VuZV9zdGF0X3ByZW5vcm0kbnVsbCA+PSBuY29sKGZhX2V4cHJfcmF3KSAqIDAuOQpwcmludChwYXN0ZTAoIlVuZGV0ZWN0ZWQgZ2VuZXMgKG51bGwgaW4gPj0gOTAlIHNhbXBsZXMpOiAiLCBzdW0odW5kZXRlY3RlZF9nZW5lcykpKQoKIyMgRGF0YSBmaWx0ZXJpbmc6IGdlbmVzIGhhdmluZyBhIG1lYW4gZXhwcmVzc2lvbiA8IDEwCmJhcmVseV9leHByZXNzZWRfZ2VuZXMgPC0gZ2VuZV9zdGF0X3ByZW5vcm0kbWVhbiA8IDEwCnByaW50KHBhc3RlMCgiQmFyZWx5IGV4cHJlc3NlZCBnZW5lcyAobWVhbiA8IDEwKTogIiwgc3VtKGJhcmVseV9leHByZXNzZWRfZ2VuZXMpKSkKCiMjIEFwcGx5IGZpbHRlcmluZyBvbiBib3RoIGNyaXRlcmlhCmRpc2NhcmRlZF9nZW5lcyA8LSB1bmRldGVjdGVkX2dlbmVzIHwgYmFyZWx5X2V4cHJlc3NlZF9nZW5lcwpwcmludChwYXN0ZTAoIkRpc2NhcmRlZCBnZW5lczogIiwgc3VtKGRpc2NhcmRlZF9nZW5lcykpKQprZXB0X2dlbmVzIDwtICFkaXNjYXJkZWRfZ2VuZXMKcHJpbnQocGFzdGUwKCJLZXB0IGdlbmVzOiAiLCBzdW0oa2VwdF9nZW5lcykpKQoKIyMgR2VuZXMgYWZ0ZXIgZmlsdGVyaW5nCmZhX2V4cHJfbG9nMl9maWx0ZXJlZCA8LSBmYV9leHByX2xvZzJba2VwdF9nZW5lcywgXQpgYGAKCiMjIyBOb3JtYWxpc2F0aW9uIGVudHJlIMOpY2hhbnRpbGxvbnMKCmBgYHtyIHNhbXBsZV9zdGFuZGFyZGlzYXRpb259CiMjIEdlbmVyYXRlIGEgZGF0YSBmcmFtZSB3aGVyZSBudWxsIHZhbHVlcyBhcmUgcmVwbGFjZWQgYnkgTkEKZmFfZXhwcl9ub251bGwgPC0gZmFfZXhwcl9sb2cyX2ZpbHRlcmVkCmZhX2V4cHJfbm9udWxsW2ZhX2V4cHJfbG9nMl9maWx0ZXJlZCA8PSAwXSA8LSBOQQpzdW0oaXMubmEoZmFfZXhwcl9ub251bGwpKQoKIyMgQ29tcHV0ZSB0aGUgM3JkIHF1YXJ0aWxlIG9mIG5vbi1udWxsIHZhbHVlcyBmb3IgZWFjaCBzYW1wbGUgYW5kIHN0b3JlIHRoZW0gaW4gYSB2ZWN0b3I6CnNhbXBsZV9xM19ub251bGwgPC0gYXBwbHkoZmFfZXhwcl9ub251bGwsIDIsIHF1YW50aWxlLCBwcm9iID0gMC43NSwgbmEucm0gPSBUUlVFKQojIHByaW50KHNhbXBsZV9xM19ub251bGwpCgojIyBDb21wdXRlIHRoZSBRMyBmb3IgYWxsIHRoZSB2YWx1ZXMsIHdoaWNoIHdpbGwgc2VydmUgYXMgdGFyZ2V0IHZhbHVlIGZvciB0aGUgc3RhbmRhcmRpc2VkIHNhbXBsZSBRMwphbGxfcTNfbm9udWxsIDwtIHF1YW50aWxlKHVubGlzdChmYV9leHByX25vbnVsbCksIHByb2IgPSAwLjc1LCBuYS5ybSA9IFRSVUUpCiMgcHJpbnQoYWxsX3EzX25vbnVsbCkKCiMjIFN0YW5kYXJkaXNlIGV4cHJlc3Npb24gb24gdGhlIHRoaXJkIHF1YXJ0aWxlIG9mIG5vbi1udWxsIHZhbHVlcwojIyBCZXdhcmUgOiBmb3IgdGhpcyBzdGFuZGFyZGl6YXRpb24gd2Uga2VlcCB0aGUgbnVsbCB2YWx1ZXMKIyMgVHJpY2sgOiB3ZSB0cmFuc3Bvc2UgdGhlIHRhYmxlIHRvIGFwcGx5IHRoZSByYXRpbyBzYW1wbGUgcGVyIHNhbXBsZSwgCiMjIGFuZCB0aGVuIHRyYW5zcG9zZSB0aGUgcmVzdWx0cyB0byBnZXQgYmFjayB0aGUgZ2VuZXMgaW4gcm93cyBhbmQgc2FtcGxlcyBpbiBjb2x1bW5zCmZhX2V4cHJfbG9nMl9zdGFuZGFyZCA8LSB0KHQoZmFfZXhwcl9sb2cyX2ZpbHRlcmVkKSAqIGFsbF9xM19ub251bGwgLyBzYW1wbGVfcTNfbm9udWxsICkKIyBxdWFudGlsZSh1bmxpc3QoZmFfZXhwcl9sb2cyX3N0YW5kYXJkKSwgcHJvYnMgPSAwLjc1LCBuYS5ybSA9IFRSVUUpCgojIyBXZSBhbHNvIGNvbXB1dGUgdGhlIHZhbHVlcyBmb3IgdGhlICJub251bGwiIHRhYmxlIGZvciAKIyMgdGhlIHNha2Ugb2YgY29tcGFyaXNvbiBhbmQgdG8gY2hlY2sgdGhhdCB0aGUgdGhpcmQgcXVhbnRpbGVzIG9mIG5vbi1udWxsIAojIyB2YWx1ZXMgYXJlIHdlbGwgaWRlbnRpY2FsIGFjcm9zcyBzYW1wbGVzLgpmYV9leHByX2xvZzJfc3RhbmRhcmRfbm9udWxsIDwtIHQodChmYV9leHByX25vbnVsbCkgKiBhbGxfcTNfbm9udWxsIC8gc2FtcGxlX3EzX25vbnVsbCApCiMgcXVhbnRpbGUodW5saXN0KGZhX2V4cHJfbG9nMl9zdGFuZGFyZF9ub251bGwpLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSkKCiMjIENvbXB1dGUgUTMgYmVmb3JlIGFuZCBhZnRlciBzdGFuZGFyZGlzYXRpb24sIGluY2x1ZGluZyBvciBub3QgdGhlIG51bGwgdmFsdWVzCnN0YW5kYXJkaXNhdGlvbl9pbXBhY3QgPC0gZGF0YS5mcmFtZSgKICBiZWZvcmVfYWxsID0gYXBwbHkoZmFfZXhwcl9sb2cyX2ZpbHRlcmVkLCAyLCBxdWFudGlsZSwgcHJvYiA9ICAwLjc1LCBuYS5ybSA9IFRSVUUpLAogIGJlZm9yZV9ub251bGwgPSBhcHBseShmYV9leHByX25vbnVsbCwgMiwgcXVhbnRpbGUsIHByb2IgPSAgMC43NSwgbmEucm0gPSBUUlVFKSwKICBhZnRlcl9ub251bCA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZF9ub251bGwsIDIsIHF1YW50aWxlLCBwcm9iID0gIDAuNzUsIG5hLnJtID0gVFJVRSksCiAgYWZ0ZXJfYWxsID0gYXBwbHkoZmFfZXhwcl9sb2cyX3N0YW5kYXJkLCAyLCBxdWFudGlsZSwgcHJvYiA9ICAwLjc1LCBuYS5ybSA9IFRSVUUpCikKCiMjIE5vdGU6IGFmdGVyIHN0YW5kYXJkaXphdGlvbiB0aGUgUTMgb2YgdGhlIGRhdGEgc2hvdyBzb21lIHZhcmlhdGlvbnMgCiMjIGJlY2F1c2Ugd2UgY29tcHV0ZSB0aGVtIGhlcmUgd2l0aCB0aGUgbnVsbCB2YWx1ZXMKa2FibGUoc3RhbmRhcmRpc2F0aW9uX2ltcGFjdCwgY2FwdGlvbiA9ICJJbXBhY3Qgb2Ygc3RhbmRhcmRpemF0aW9uIG9uIHRoZSB0aGlyZCBxdWFudGlsZSAoUTMpIHBlciBzYW1wbGUuIFRoaXJkIHF1YW50aWxlcyBhcmUgY29tcHV0ZWQgYmVmb3JlIGFuZCBhZnRlciBzdGFuZGFyZGlzYXRpb24sIHdpdGggZWl0aGVyIGFsbCB0aGUgdmFsdWVzIG9mIHRoZSBmaWx0ZXJlZCB0YWJsZSwgb3Igb25seSB0aGUgbm9uLW51bGwgdmFsdWVzLiAiKQpgYGAKCiMjIDMuIExlcyBkb25uw6llcyBub3JtYWxpc8OpZXMKCioqKkEgdm91cyBkZSBqb3VlciEqKioKCiMjIyBTdGF0aXN0aXF1ZXMgcGFyIGfDqG5lIGFwcsOocyBub3JtYWxpc2F0aW9uCgpHw6luw6lyZXogdW4gZGF0YS5mcmFtZSBhdmVjIHVuZSBsaWduZSBwYXIgZ8OobmUgw6AgcGFydGlyIGR1IHRhYmxlYXUgZGUgZG9ubsOpZXMgbm9ybWFsaXPDqWVzLCBhdmVjIGxlcyBzdGF0aXN0aXF1ZXMgc3VpdmFudGVzICh1bmUgc3RhdGlzdGlxdWUgcGFyIGNvbG9ubmUpOgoKLSBtb3llbm5lCi0gdmFyaWFuY2UKLSDDqWNhcnQtdHlwZQotIGNvZWZmaWNpZW50IGRlIHZhcmlhdGlvbiAow6ljYXJ0LXR5cGUgZGl2aXPDqSBwYXIgbGEgbW95ZW5uZSkKLSBpbnRlcnZhbGxlIGludGVyLXF1YXJ0aWxlcwotIG1pbmltdW0KLSBtw6lkaWFuZQotIG1heGltdW0KCmBgYHtyIGdlbmVfc3RhdF9wb3N0X25vcm19CgojIyBHZW5lLXdpc2Ugc3RhdGlzdGljcyBhZnRlciBub3JtYWxpc2F0aW9uCmdlbmVfc3RhdF9ub3JtIDwtIGRhdGEuZnJhbWUoCiAgbWVhbiA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCwgMSwgbWVhbiwgbmEucm0gPSBGKSwKICB2YXIgPSBhcHBseShmYV9leHByX2xvZzJfc3RhbmRhcmQsIDEsIHZhciwgbmEucm0gPSBUUlVFKSwKICBzZCA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCwgMSwgc2QsIG5hLnJtID0gRiksCiAgQ1YgPSBOQSwKICBtaW4gPSBhcHBseShmYV9leHByX2xvZzJfc3RhbmRhcmQsIDEsIG1pbiwgbmEucm0gPSBUUlVFKSwKICBRMSA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCwgMSwgcXVhbnRpbGUsIHAgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogIG1lZGlhbiA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCwgMSwgbWVkaWFuLCBuYS5ybSA9IFRSVUUpLAogIFEzID0gYXBwbHkoZmFfZXhwcl9sb2cyX3N0YW5kYXJkLCAxLCBxdWFudGlsZSwgcCA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgbWF4ID0gYXBwbHkoZmFfZXhwcl9sb2cyX3N0YW5kYXJkLCAxLCBtYXgsIG5hLnJtID0gVFJVRSksCiAgbnVsbCA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCA9PSAwLCAxLCBzdW0sIG5hLnJtID0gVFJVRSkKICApCmdlbmVfc3RhdF9ub3JtJENWIDwtIGdlbmVfc3RhdF9ub3JtJHNkIC8gZ2VuZV9zdGF0X25vcm0kbWVhbgoKYGBgCgojIyMgQW5ub3RhdGlvbiBkZXMgZ8OobmVzCgpDaGFxdWUgZ8OobmUgw6l0YW50IGRvbm7DqSBwYXIgc29uIGlkZW50aWZpYW50IGRhbnMgbGEgYmFzZSBkZSBkb25uw6llcyBFTlNFTUJMIHZvdXMgdXRpbGlzZXJleiBsZSBgcGFxdWV0IGJpb21hUnQgZGUgYmlvY29uZHVjdG9yYCBwb3VyIGFqb3V0ZXIgZGVzIGFubm90YXRpb25zIDogc3ltYm9sZSwgY2hyb21vc29tZSwgY29vcmRvbm7DqWVzIGfDqW5vbWlxdWVzLCBicmluLiAKU3VpdmV6IHBhcyDDoCBwYXMgbGEgbcOpdGhvZGUgcHJvcG9zw6llOgoKIC0gc8OpbGVjdGlvbm5leiBsYSBiYXNlIGRlIGRvbm7DqWVzIEVOU0VNQkwgYXZlYyBsYSBmb25jdGlvbiBgdXNlTWFydCgpYC4gQXR0ZW50aW9uIMOgIGNob2lzaXIgbGUgYm9uIGfDqW5vbWUgYXZlYyBsJ2FndW1lbnQgYGRhdGFzZXRgOiBgbW11c2N1bHVzX2dlbmVfZW5zZW1ibGAKIAogLSBhdmVjIGxhIGZvbmN0aW9uIGBnZXRCTSgpYCByw6ljdXDDqXJleiBkZSBsYSBiYXNlIGRlIGRvbm7DqWVzIEVOU0VNQkwgbGVzIGNoYW1wcyBkZW1hbmTDqXMgKCoqKnBvdXIgc3ltYm9sZSB1dGlsaXNleiBleHRlcm5hbF9nZW5lX25hbWUqKiopIGVuIGFwcGxpcXVhbnQgImVuc2VtYmxfZ2VuZWlkIiBwb3VyIGwnYWd1bWVudCBgZmlsdGVyYCBldCBlbiBpbmRpcXVhbnQgcG91ciBsJ2FyZ3VtZW50IGB2YWx1ZXNgIGxlIHZlY3RldXIgZGVzIGlkZW50aWZpYW50cyBkZXMgZ8OobmVzIHByw6lzZW50cyBkYW5zIGxlIGRhdGFmcmFtZSBgZ2VuZV9zdGF0X25vcm1gLiBWb3VzIG9idGVuZXogdW4gZGF0YWZyYW1lLgogCkEgcHLDqXNlbnQsIGFqb3V0ZXogYXUgZGF0YWZyYW1lIGBnZW5lX3N0YXRfbm9ybWAgZW4gMcOocmVzIGNvbG9ubmVzIGxlcyBhbm5vdGF0aW9ucyByZXRyb3V2w6llcyBncsOiY2Ugw6AgYmlvbWFSdC4gQXR0ZW50aW9uLCBjZXJ0YWlucyBnw6huZXMgbmUgc29udCBwYXMgcmV0cm91dsOpcyBkYW5zIGxhIHZlcnNpb24gZCdFTlNFTUJMIHN1ciBiaW9tYVJ0IGRvbmMgbGFpc3NleiBkZXMgTkEgY29tbWUgZG9ubsOpZXMgbWFucXVhbnRlcyBkYW5zIGNlIGNhcy4gTm91cyB2b3VzIHJlY29tbWFuZG9ucyBkJ3V0aWxpc2VyIGxhIGZ1bmN0aW9uIGBtZXJnZSgpYCBvdSBgbGVmdF9qb2luKClgIGRlIGRwbHlyIHBvdXIgZnVzaW9ubmVyIGxlcyBkZXV4IGRhdGFmcmFtZXMgZW4gdW4gc2V1bC4KIApgYGB7ciBnZW5lX2Fubm90YXRpb25zfQplbnNlbWJsIDwtIHVzZU1hcnQoIkVOU0VNQkxfTUFSVF9FTlNFTUJMIiwgaG9zdCA9ICJ3d3cuZW5zZW1ibC5vcmciLCBkYXRhc2V0ID0gIm1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiKQoKZ2VuZXMgPC1nZXRCTShhdHRyaWJ1dGVzID0gYygiZW5zZW1ibF9nZW5lX2lkIiwgImV4dGVybmFsX2dlbmVfbmFtZSIsICJjaHJvbW9zb21lX25hbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RhcnRfcG9zaXRpb24iLCAiZW5kX3Bvc2l0aW9uIiwgInN0cmFuZCIpLCAKICAgICAgICAgICAgICAgIGZpbHRlciA9ICJlbnNlbWJsX2dlbmVfaWQiLAogICAgICAgICAgICAgICAgdmFsdWVzID0gcm93Lm5hbWVzKGdlbmVfc3RhdF9ub3JtKSwKICAgICAgICAgICAgICAgIG1hcnQgPSBlbnNlbWJsCiAgICAgICAgICAgICAgKQogIAogCmdlbmVfc3RhdF9ub3JtIDwtIG1lcmdlKGdlbmVzLCBnZW5lX3N0YXRfbm9ybSwgYnkueCA9ICJlbnNlbWJsX2dlbmVfaWQiLCBieS55ID0gMCwgc29ydCA9IEZBTFNFKQoKa2FibGUoZ2VuZV9zdGF0X25vcm1bMTAwOjEwOSwgXSwgY2FwdGlvbiA9ICJHZW5lLXdpc2Ugc3RhdGlzdGljcyBhZnRlciBub3JtYWxpc2F0aW9uIikKYGBgCgoqKkNoYWxsZW5nZSBmYWxjdWx0YXRpZjoqKgoKRW5maW4sIHLDqW9yZG9ubmV6IGxlcyBnw6huZXMgcGFyIHBvc2l0aW9uIGfDqW5vbWlxdWUgZXQgYWZmaWNoZXogbGVzIGxpZ25lcyA1IHByZW1pw6hyZXMgZXQgIDUgZGVybmnDqHJlcyBsaWduZXMgZGUgY2UgdGFibGVhdSBkZSBzdGF0aXN0aXF1ZXMuIAoKYGBge3IgZGlzcGxheSBmcmFnbWVudCBvZiBnZW5lLXdpc2Ugc3RhdHMgYWZ0ZXIgbm9ybX0KCmdlbmVfc3RhdF9ub3JtIDwtIGdlbmVfc3RhdF9ub3JtW29yZGVyKGdlbmVfc3RhdF9ub3JtJGNocm9tb3NvbWVfbmFtZSwgZ2VuZV9zdGF0X25vcm0kc3RhcnRfcG9zaXRpb24pLF0KCmthYmxlKGdlbmVfc3RhdF9ub3JtW2MoMTo1LCAobnJvdyhnZW5lX3N0YXRfbm9ybSktNCk6bnJvdyhnZW5lX3N0YXRfbm9ybSkpLCBdLCBjYXB0aW9uID0gIkdlbmUtd2lzZSBzdGF0aXN0aWNzIGFmdGVyIG5vcm1hbGlzYXRpb24uIikKCmBgYAoKCiMjIyBEaXN0cmlidXRpb24gZGVzIGRvbm7DqWVzCgotIERlc3NpbmV6IHNvdXMgZm9ybWUgZCd1biBoaXN0b2dyYW1tZSBsYSBkaXN0cmlidXRpb24gZGVzIHZhbGV1cnMgYXByw6hzIG5vcm1hbGlzYXRpb24gKHRvdXMgw6ljaGFudGlsbG9ucyBjb25mb25kdXMpCgpgYGB7ciBmYV9leHByX25vcm1fZGlzdHJpYiwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NSwgb3V0LndpZHRoPSI3MCUiLCBmaWcuY2FwPSJEaXN0cmlidXRpb24gb2YgZXhwcmVzc2lvbiB2YWx1ZXMgKGxvZzIgY291bnRzKSBhZnRlciBnZW5lIGZpbHRlcmluZyBhbmQgc3RhbmRhcmRpc2F0aW9uIG9uIHRoZSBzYW1wbGUtd2lzZSB0aGlyZC1xdWFydGlsZSBvZiBub24tbnVsbCB2YWx1ZXMuIFRoZSB2ZXJ0aWNhbCBsaW5lIGhpZ2hsaWdodHMgdGhlIG1lYW4gdmFsdWUuICJ9Cmhpc3QodW5saXN0KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCksIAogICAgIGJyZWFrcyA9IHNlcShmcm9tID0gMCwgdG8gPSBtYXgoZmFfZXhwcl9sb2cyX3N0YW5kYXJkKSArIDEsIGJ5ID0gMC4yNSksCiAgICAgeGxhYiA9ICJsb2cyKGNvdW50cykgYWZ0ZXIgc3RhbmRhcmRpc2F0aW9uIiwgCiAgICAgeWxhYiA9ICJudW1iZXIgb2YgZ2VuZXMgYWZ0ZXIgZmlsdGVyaW5nIiwKICAgICBjb2wgPSAiI0JCRERGRiIsCiAgICAgbGFzID0gMSwgY2V4LmF4aXMgPSAwLjgsCiAgICAgbWFpbiA9ICJkaXN0cmlidXRpb24gYWZ0ZXIgc3RhbmRhcmRpc2F0aW9uIikKYWJsaW5lKHYgPSBtZWFuKGZhX2V4cHJfbG9nMl9zdGFuZGFyZCksIGNvbCA9ICJkYXJrZ3JlZW4iLCBsd2QgPSAyKQoKYGBgCgotIERlc3NpbmV6IHVuIGJveCBwbG90IGRlcyDDqWNoYW50aWxsb25zIGF2YW50IGV0IGFwcsOocyBub3JtYWxpc2F0aW9uLCBldCBjb21tZW50ZXogbGEgZmHDp29uIGRvbnQgbCdlZmZldCBkZSBsYSBub3JtYWxpc2F0aW9uIGFwcGFyYcOudCBzdXIgY2VzIGdyYXBoaXF1ZXMuIAoKYGBge3IgYm94cGxvdHNfc3RhbmRhcmRpc2F0aW9uX2ltcGFjdCwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyLCBvdXQud2lkdGg9IjEwMCUiLCBmaWcuY2FwPSJCb3ggcGxvdHMgc2hvd2luZyB0aGUgaW1wYWN0IG9mIG5vcm1hbGlzYXRpb24ifQojIyMjIEJveCBwbG90cyB0byBzaG93IG5vcm1hbGlzYXRpb24gaW1wYWN0ICMjIyMKcGFyKG1hciA9IGMoNCw2LDQsMSkpICMjIFNldCB0aGUgbWFyZ2lucwpwYXIobWZyb3cgPSBjKDIsMikpCmJveHBsb3QoZmFfZXhwcl9sb2cyX2ZpbHRlcmVkLCAKICAgICAgICBob3Jpem9udGFsID0gVFJVRSwKICAgICAgICB4bGFiID0gImxvZzIoY291bnRzKSIsIAogICAgICAgIGxhcyA9IDEsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiQmVmb3JlIHN0YW5kYXJkaXNhdGlvblxuYWxsIHZhbHVlcyIpCmJveHBsb3QoZmFfZXhwcl9ub251bGwsIAogICAgICAgIGhvcml6b250YWwgPSBUUlVFLCAKICAgICAgICB4bGFiID0gImxvZzIoY291bnRzKSIsIAogICAgICAgIGxhcyA9IDEsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiQmVmb3JlIHN0YW5kYXJkaXNhdGlvblxuemVyb3MgZGlzY2FyZGVkIikKYm94cGxvdChmYV9leHByX2xvZzJfc3RhbmRhcmRfbm9udWxsLCAKICAgICAgICBob3Jpem9udGFsID0gVFJVRSwgCiAgICAgICAgeGxhYiA9ICJsb2cyKGNvdW50cykiLCAKICAgICAgICBsYXMgPSAxLCAKICAgICAgICBjb2wgPSBmYV9tZXRhJGNvbG9yLCAKICAgICAgICBtYWluID0gIlN0YW5kYXJkaXNlZFxuemVyb3MgZGlzY2FyZGVkIikKYm94cGxvdChmYV9leHByX2xvZzJfc3RhbmRhcmQsIAogICAgICAgIHhsYWIgPSAibG9nMihjb3VudHMpIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiU3RhbmRhcmRpc2VkXG5hbGwgdmFsdWVzIikKcGFyKG1mcm93ID0gYygxLCAxKSkKcGFyKG1hciA9IGMoNCw1LDUsMSkpCgpgYGAKCiMjIDQuIEFuYWx5c2UgZGUgcmVncm91cGVtZW50IGRlcyBkb25uw6llcwoKIyMjIEZpbHRyYWdlIDIgOiBzw6lsZWN0aW9uIGRlIGfDqG5lcyBkJ2V4cHJlc3Npb24gw6lsZXbDqWUgZXQgdmFyaWFibGUKClBvdXIgcsOpZHVpcmUgbGUgbm9tYnJlIGRlIGfDqG5lcywgbm91cyBhbGxvbnMgw6ljYXJ0ZXIgbGVzIGfDqG5lcyBmYWlibGVtZW50IGV4cHJpbcOpcyAobG9nMiBtb3llbiBpbmbDqXJpZXVyIMOgIDQpLCBldCBuZSByZXRlbmlyIHF1ZSBjZXV4IHF1aSBtb250cmVudCBkZXMgdmFyaWF0aW9ucyBpbXBvcnRhbnRlcyBlbnRyZSDDqWNoYW50aWxsb25zLiBQb3VyIGNlIGRlcm5pZXIgY3JpdMOocmUsIG5vdXMgbm91cyBiYXNvbnMgc3VyIGxlIGNvZWZmaWNpZW50IGRlIHZhcmlhdGlvbiBhZmluIGRlIHJlbGF0aXZpc2VyIGxhIGRpc3BlcnNpb24gKMOpY2FydCB0eXBlKSBwYXIgcmFwcG9ydCDDoCBsYSB0ZW5kYW5jZSBjZW50cmFsZSAobW95ZW5uZSkuIAoKU8OpbGVjdGlvbm5leiBsZXMgZ8OobmVzIGF5YW50IHVuIG5pdmVhdSBsb2cyIG1veWVuIG1pbmltYWwgc3Vww6lyaWV1ciDDoCAzICgkcyA+IDMkKSBldCB1biBjb2VmZmljaWVudCBkZSB2YXJpYXRpb24gc3Vww6lyaWV1ciDDoCAwLjUgKCRDViA+IDAuNSQpLiBOb3RlOiBjZXMgdmFsZXVycyBzb250IHBhcmZhaXRlbWVudCBhcmJpdHJhaXJlcywgZWxsZXMgb250IMOpdMOpIGNob2lzaWVzIHBvdXIgb2J0ZW5pciB1biBub21icmUgcmFpc29ubmFibGUgZGUgZ8OobmVzLiAKCmBgYHtyIGdlbmVfc2VsZWN0aW9ufQojIyBDb21wdXRlIGEgQm9vbGVhbiB2ZWN0b3IgaW5kaWNhdGluZyB3aGV0aGVyIGVhY2ggZ2VuZSBwYXNzZXMgb3Igbm90IHRoZSBleHByZXNzaW9uIGxldmVsIHRocmVzaG9sZApoaWdoX2V4cHJlc3Npb24gPC0gZ2VuZV9zdGF0X25vcm0kbWVhbiA+IDMKIyB0YWJsZShoaWdoX2V4cHJlc3Npb24pICMgY291bnQgbnVtYmVyIG9mIGdlbmVzIHdpdGggaGlnaC93ZWFrIGV4cHJlc3Npb24KCiMjIENvbXB1dGUgYSBCb29sZWFuIHZlY3RvciBpbmRpY2F0aW5nIHdoZXRoZXIgZWFjaCBnZW5lIHBhc3NlcyBvciBub3QgdGhlIHZhcmlhdGlvbiBjb2VmZmljaWVudCB0aHJlc2hvbGQKaGlnaF92YXJpYXRpb24gPC0gZ2VuZV9zdGF0X25vcm0kQ1YgPiAwLjUKIyB0YWJsZShoaWdoX3ZhcmlhdGlvbikgIyBjb3VudCBudW1iZXIgb2YgZ2VuZXMgd2l0aCB3ZWFrIGhpZ2ggY29lZmZmaWNpZW50IG9mIHZhcmlhdGlvbgoKIyMgQ29tcHV0ZSBhIEJvb2xlYW4gdmVjdG9yIGluZGljYXRpbmcgd2hldGhlciBlYWNoIGdlbmUgcGFzc2VzIG9yIG5vdCB0aGUgdmFyaWFuY2UgdGhyZXNob2xkCiMgaGlnaF92YXJpYW5jZSA8LSBnZW5lX3N0YXRfbm9ybSR2YXIgPiAyCiMgdGFibGUoaGlnaF92YXJpYW5jZSkgIyBjb3VudCBudW1iZXIgb2YgZ2VuZXMgd2l0aCB3ZWFrIGhpZ2ggdmFyaWFuY2UKCiMjIFNlbGVjdCBnZW5lcyBoYXZpbmcgYm90aCBhIGhpZ2ggbWVhbiBleHByZXNzaW9uIGFuZCBhIGhpZ2ggdmFyaWF0aW9uIGNvZWZmaWNpZW4Kc2VsZWN0ZWRfZ2VuZXMgPC0gaGlnaF92YXJpYXRpb24gJiBoaWdoX2V4cHJlc3Npb24KIyB0YWJsZShzZWxlY3RlZF9nZW5lcykgIyBjb3VudCBudW1iZXIgb2YgZ2VuZXMgd2l0aCB3ZWFrIGhpZ2ggY29lZmZmaWNpZW50IG9mIHZhcmlhdGlvbgpwcmludChwYXN0ZTAoIlNlbGVjdGVkIGdlbmVzOiAiLCBzdW0oc2VsZWN0ZWRfZ2VuZXMpKSkKCiMjIENyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgc2VsZWN0ZWQgZ2VuZXMKZmFfZXhwcl9zZWxlY3RlZCA8LSBmYV9leHByX2xvZzJfc3RhbmRhcmRbc2VsZWN0ZWRfZ2VuZXMsIF0KCmBgYAoKRGVzc2luZXogZGVzIGhpc3RvZ3JhbW1lcyBkZXMgdmFsZXVycyBkJ2V4cHJlc3Npb24gYXZhbnQgZXQgYXByw6hzIGNldHRlIHPDqWxlY3Rpb24gZGUgZ8OobmVzLCBldCBjb21tZW50ZXogbGVzIGRpZmbDqXJlbmNlcy4gCgpgYGB7ciBoaXN0X2V4cHJfc2VsZWN0ZWRfZ2VuZXMsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIG91dC53aWR0aD0iMTAwJSIsIGZpZy5jYXA9IkRpc3RyaWJ1dGlvbiBvZiBleHByZXNzaW9uIHZhbHVlcyBiZWZvcmUgYW5kIGFmdGVyIGdlbmUgc2VsZWN0aW9uIn0KIyMjIyBIaXN0b2dyYW1zIG9mIGV4cHJlc3Npb24gYmVmb3JlIGFuZCBhZnRlciBnZW5lIHNlbGVjdGlvbiAjIyMjCgpwYXIobWZyb3cgPSBjKDIsMSkpCmhpc3QodW5saXN0KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCksIAogICAgIGJyZWFrcyA9IHNlcShmcm9tID0gMCwgdG8gPSBtYXgoZmFfZXhwcl9sb2cyX3N0YW5kYXJkKSArIDEsIGJ5ID0gMC4yNSksCiAgICAgbGFzID0gMSwgCiAgICAgY2V4LmF4aXMgPSAwLjgsIAogICAgIG1haW4gPSAiU3RhbmRhcmRpemVkIHZhbHVlcyBiZWZvcmUgZ2VuZSBzZWxlY3Rpb24iLAogICAgIGNvbCA9ICAiI0REQkJGRiIpCgpoaXN0KHVubGlzdChmYV9leHByX3NlbGVjdGVkKSwgCiAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAwLCB0byA9IG1heChmYV9leHByX2xvZzJfc3RhbmRhcmQpICsgMSwgYnkgPSAwLjI1KSwKICAgICBsYXMgPSAxLCAKICAgICBjZXguYXhpcyA9IDAuOCwgCiAgICAgbWFpbiA9ICJTdGFuZGFyZGl6ZWQgdmFsdWVzIGFmdGVyIGdlbmUgc2VsZWN0aW9uIiwKICAgICBjb2wgPSAgIiNGRkREQkIiKQoKcGFyKG1mcm93ID0gYygxLDEpKQoKCiMgIyMgU29tZSBxdWljayBjaGVja3M6IHRoZSBzZWxlY3Rpb24gb2YgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIHNlbGVjdCB0aG9zZSBoYXZpbmcgbWFueSB6ZXJvcyAtIGFuZCBoaWdoIHZhbHVlcyBpbiBvdGhlciBzYW1wbGVzCiMgaGlzdCh1bmxpc3QoZmFfZXhwcl9sb2cyX2ZpbHRlcmVkW2hpZ2hfZXhwcmVzc2lvbiwgXSksIGJyZWFrcz0xMDApCiMgaGlzdCh1bmxpc3QoZmFfZXhwcl9sb2cyX2ZpbHRlcmVkW2hpZ2hfdmFyaWF0aW9uLCBdKSwgYnJlYWtzPTEwMCkKIyBoaXN0KHVubGlzdChmYV9leHByX2xvZzJfZmlsdGVyZWRbIWhpZ2hfdmFyaWF0aW9uLCBdKSwgYnJlYWtzPTEwMCkKIyBoaXN0KHVubGlzdChmYV9leHByX2xvZzJfZmlsdGVyZWRbc2VsZWN0ZWRfZ2VuZXMsIF0pLCBicmVha3M9MTAwKQpgYGAKCkRlc3NpbmV6IHVuIGJveCBwbG90IHBhciDDqWNoYW50aWxsb24gZGVzIHZhbGV1cnMgZCdleHByZXNzaW9uIGF2YW50IGV0IGFwcsOocyBzw6lsZWN0aW9uIGRlcyBnw6huZXMsIGV0IGNvbW1lbnRleiBsZSByw6lzdWx0YXQuIAoKYGBge3IgYm94cGxvdHNfZXhwcl9zZWxlY3RlZF9nZW5lcywgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTUsIG91dC53aWR0aD0iNjAlIiwgZmlnLmNhcD0iQm94IHBsb3RzIG9mIHN0YW5kYXJkaXNlZCBleHByZXNzaW9uIHZhbHVlcyBiZWZvcmUgYW5kIGFmdGVyIGdlbmUgc2VsZWN0aW9uLiAifQojIyMjIEhpc3RvZ3JhbSBvZiBleHByZXNzaW9uIGFmdGVyIGdlbmUgc2VsZWN0aW9uICMjIyMKCnBhcihtZnJvdyA9IGMoMSwyKSkKCmJveHBsb3QoZmFfZXhwcl9sb2cyX3N0YW5kYXJkLCAKICAgICAgICBob3Jpem9udGFsID0gVFJVRSwKICAgICAgICB4bGFiID0gImxvZzIoY291bnRzKSIsIAogICAgICAgIGxhcyA9IDEsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiQmVmb3JlIGdlbmUgc2VsZWN0aW9uXG5zdGFuZGFyZGlzZWQgdmFsdWVzIikKYm94cGxvdChmYV9leHByX3NlbGVjdGVkLCAKICAgICAgICBob3Jpem9udGFsID0gVFJVRSwKICAgICAgICB4bGFiID0gImxvZzIoY291bnRzKSIsIAogICAgICAgIGxhcyA9IDEsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiQWZ0ZXIgZ2VuZSBzZWxlY3Rpb25cbnN0YW5kYXJkaXNlZCB2YWx1ZXMiKQoKcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKCgojIyMgQUNQCgpEZXNzaW5leiB1biBwbG90IEFDUCBkZXMgw6ljaGFudGlsbG9ucyBlbiBsZXMgY29sb3JhbnQgcGFyIGNvbmRpdGlvbiBhdmFudCBldCBhcHLDqHMgbm9ybWFsaXNhdGlvbi4KCi0gYXZlYyBsZXMgY29tcHRhZ2VzIGJydXRzIGRlIGxhIG1hdHJpY2UgZCdleHByZXNzaW9uIGluaXRpYWxlICgkZmFfZXhwciQpCgpgYGB7ciBhY3BfcmF3X2FsbF9nZW5lcywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OCwgb3V0LndpZHRoPSI2MCUiLCBmaWcuY2FwPSJQQyBwbG90IG9mIHRoZSBzYW1wbGVzIGZyb20gdGhlIHJhdyBleHByZXNzaW9uIHZhbHVlcyBvZiBhbGwgZ2VuZXMuICJ9CiMjIFJhdyBleHByZXNzaW9uIHZhbHVlcywgYWxsIGdlbmVzCm1hX3BjYV9yYXdfdHQgPC0gUENBKHQoZmFfZXhwcl9yYXcpLCAKICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLnVuaXQgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICBncmFwaCA9IEZBTFNFKQojIHBsb3QobWFfcGNhX3Jhd190dCwgY2hvaXggPSAiaW5kIikKZnZpel9wY2FfaW5kKG1hX3BjYV9yYXdfdHQsIGNvbC5pbmQgPSBmYV9tZXRhWywgImNvbG9yIl0pCgpgYGAKCjwhLS0gLSBhdmVjIGxlcyBjb21wdGFnZXMgYnJ1dHMsIG1haXMgZW4gcmVzdHJlaWduYW50IGwnYW5hbHlzZSBhdXggZ8OobmVzIHPDqWxlY3Rpb25uw6lzICgkZmFfZXhwcl9yYXdbc2VsZWN0ZWRfZ2VuZXMsXSQpIC0tPgoKCjwhLS0gYGBge3IgYWNwX3Jhd19zZWxlY3RlZF9nZW5lcywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OCwgb3V0LndpZHRoPSI2MCUiLCBmaWcuY2FwPSJQQyBwbG90IG9mIHRoZSBzYW1wbGVzIGZyb20gdGhlIHJhdyBleHByZXNzaW9uIHZhbHVlcyBvZiBzZWxlY3RlZCBnZW5lcy4gIn0gLS0+CjwhLS0gIyMgUmF3IHZhbHVlcyB3aXRoIG9ubHkgdGhlIHNlbGVjdGVkIGdlbmVzIC0tPgo8IS0tIG1hX3BjYV9yYXdfc2VsIDwtIFBDQSh0KGZhX2V4cHJfcmF3W3NlbGVjdGVkX2dlbmVzLF0pLCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLnVuaXQgPSBGQUxTRSwgIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICBncmFwaCA9IEZBTFNFKSAtLT4KPCEtLSBmdml6X3BjYV9pbmQobWFfcGNhX3Jhd19zZWwsIGNvbC5pbmQgPSBmYV9tZXRhWywgImNvbG9yIl0pIC0tPgo8IS0tIGBgYCAtLT4KCgotIGF2ZWMgbGEgbWF0cmljZSBkZSB2YWxldXJzIGZpbHRyw6llcyBldCBhcHLDqHMgdHJhbnNmb3JtYXRpb24gbG9nMgoKYGBge3IgYWNwX25vcm1fZmlsdGVyZWRfZ2VuZXMsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTgsIG91dC53aWR0aD0iNjAlIiwgZmlnLmNhcD0iUEMgcGxvdCBvZiB0aGUgc2FtcGxlcyBmcm9tIHN0YW5kYXJkaXNlZCB2YWx1ZXMgYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24uICJ9Cm1hX3BjYV9maWx0ZXJlZCA8LSBQQ0EodChmYV9leHByX2xvZzJfZmlsdGVyZWQpLCBzY2FsZS51bml0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCiMgcGxvdChtYV9wY2FfZmlsdGVyZWQsIGNob2l4ID0gInZhciIpCiMgcGxvdChtYV9wY2Ffc2VsLCBjaG9peCA9ICJpbmQiKQpmdml6X3BjYV9pbmQobWFfcGNhX2ZpbHRlcmVkLCBjb2wuaW5kID0gZmFfbWV0YVssICJjb2xvciJdKQpgYGAKCi0gYXZlYyBsYSBtYXRyaWNlIGZpbmFsZSAodHJhbnNmb3JtYXRpb24gbG9nMiwgZmlsdHJlIGRlcyBnw6huZXMgbm9uLWTDqXRlY3TDqXMsIHN0YW5kYXJkaXNhdGlvbiBldCBzw6lsZWN0aW9uIGRlcyBnw6huZXMgZm9ydGVtZW50IGV4cHJpbcOpcyBldCDDoCBoYXV0IGNvZWZmaWNpZW50IGRlIHZhcmlhdGlvbikKCmBgYHtyIGFjcF9ub3JtX3NlbGVjdGVkX2dlbmVzLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IlBDIHBsb3Qgb2YgdGhlIHNhbXBsZXMgZnJvbSBzdGFuZGFyZGlzZWQgdmFsdWVzIGFmdGVyIGdlbmUgc2VsZWN0aW9uLiAifQptYV9wY2Ffc2VsIDwtIFBDQSh0KGZhX2V4cHJfc2VsZWN0ZWQpLCBzY2FsZS51bml0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCiMgcGxvdChtYV9wY2Ffc2VsLCBjaG9peCA9ICJ2YXIiKQojIHBsb3QobWFfcGNhX3NlbCwgY2hvaXggPSAiaW5kIikKZnZpel9wY2FfaW5kKG1hX3BjYV9zZWwsIGNvbC5pbmQgPSBmYV9tZXRhWywgImNvbG9yIl0pCmBgYAoKIyMjIENsdXN0ZXJpbmcKCi0gQ2FsY3VsZXogbGVzIG1hdHJpY2VzIGRlIGRpc3RhbmNlIGVudHJlIMOpY2hhbnRpbGxvbnMsIGVuIHV0aWxpc2FudCByZXNwZWN0aXZlbWVudCBsZXMgZGlzdGFuY2VzIGV1Y2xpZGllbm5lIChgZGlzdCgpYCksIGNvZWZmaWNpZW50IGRlIFBlYXJzb24gKGBjb3IoLCBtZXRob2QgPSAicGVhcnNvbiIpYCkgIGV0IGRlIFNwZWFybWFuIChgY29yKCwgbWV0aG9kID0gInNwZWFybWFuIilgKS4KCgpgYGB7ciBzYW1wbGVfZGlzdGFuY2VzfQojIyMjIFNhbXBsZSBkaXN0YW5jZXMgIyMjIwpkaXN0X2V1Y19zZWwgPC0gZGlzdCh0KGZhX2V4cHJfc2VsZWN0ZWQpKQpjb3JfcGVhcnNvbl9zZWwgPC0gYXMuZGlzdCgxIC0gY29yKGZhX2V4cHJfc2VsZWN0ZWQpKQpjb3Jfc3BlYXJtYW5fc2VsIDwtIDEgLSBhcy5kaXN0KGNvcihmYV9leHByX3NlbGVjdGVkLCBtZXRob2QgPSAic3BlYXJtYW4iKSkKYGBgCgoKLSBFZmZlY3R1ZXogdW4gY2x1c3RlcmluZyBoacOpcmFyY2hpcXVlIGRlcyDDqWNoYW50aWxsb25zLCBlbiB1dGlsaXNhbnQgbGUgY3JpdMOocmUgZGUgV2FyZCAoYHdhcmQuZDJgKSBwb3VyIGwnYWdnbG9tw6lyYXRpb24uIENvbXBhcmV6IGxlcyBhcmJyZXMgZCfDqWNoYW50aWxsb25zIG9idGVudXMgYXZlYyBjZXMgdHJvaXMgbcOpdHJpcXVlcyBldCBjaG9pc2lzc2V6IGNlbGxlIHF1aSB2b3VzIHBhcmHDrnQgbGEgcGx1cyBwZXJ0aW5lbnRlLgoKCmBgYHtyIHNhbXBsZV9jbHVzdGVyaW5nLCBmaWcud2lkdGg9MTIsIHBsb3QuaGVpZ2h0PTUsIG91dC53aWR0aD0iMTAwJSIsIGZpZy5jYXA9IlNhbXBsZSB0cmVlIHdpdGggdGhyZWUgYWx0ZXJuYXRpdmUgZGlzdGFuY2UgbWV0cmljczogRXVjbGlkaWFudCBkaXN0YW5jZSAobGVmdCksIFBlYXJzb24gY29ycmVsYXRpb24gKGNlbnRlciksIFNwZWFybWFuIGNvcnJlbGF0aW9uIChyaWdodCkuPyAifQojIyMjIFNhbXBsZSBjbHVzdGVyaW5nICMjIyMKcGFyKG1mcm93ID0gYygxLDMpKQpwbG90KGhjbHVzdChkaXN0X2V1Y19zZWwpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJldWNsaWRlYW4gZGlzdGFuY2UiKQpwbG90KGhjbHVzdChjb3JfcGVhcnNvbl9zZWwpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJwZWFyc29uIikKcGxvdChoY2x1c3QoY29yX3NwZWFybWFuX3NlbCksIGhhbmcgPSAtMSwKICAgICBtYWluID0gInNwZWFybWFuIikKcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKCgotIEVmZmVjdHVleiB1biBjbHVzdGVyaW5nIGhpw6lyYXJjaGlxdWUgZGVzIGfDqG5lcyBlbiB1dGlsaXNhbnQgbGEgZGlzdGFuY2UgYmFzw6llIHN1ciBsZSBjb2VmZmljaWVudCBkZSBQZWFyc29uIGV0IGxlIGNyaXTDqHJlIGRlIFdhcmQKCmBgYHtyIGdlbmVfY2x1c3RlcmluZ30KY29yX3BlYXJzb25fZ2VuZSA8LSBhcy5kaXN0KDEgLSBjb3IodChmYV9leHByX3NlbGVjdGVkKSkpCnBsb3QoaGNsdXN0KGNvcl9wZWFyc29uX2dlbmUpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJnw6huZXMiKQpgYGAKCgotIERlc3NpbmV6IHVuIGFyYnJlIGF2ZWMgbGUgcsOpc3VsdGF0IGR1IGNsdXN0ZXJpbmcgZGVzIGfDqG5lcyBldCBjb21tZW50ZXogc2Egc3RyY3V0dXJlLiBTaSB2b3VzIGRldmlleiBjaG9pc2lyIGRlIGZhw6dvbiBhcmJpdHJhaXJlIHVuIG5vbWJyZSBkZSBjbHVzdGVycywgcXVlIGNob2lzaXJpZXotdm91cyA/IFBvdXJxdW9pID8gUGFzIGRlIHBhbmlxdWUsIG5vdXMgcG91dm9ucyBhc3N1bWVyIGljaSBxdWUgbGEgcsOpcG9uc2UgY29tcG9ydGUgdW5lIHBhcnQgZGUgc3ViamVjdGl2aXTDqS4gCgpgYGB7ciBnZW5lX3RyZWV9CnBsb3QoaGNsdXN0KGNvcl9wZWFyc29uX2dlbmUpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJnw6huZXMiKQpyZWN0LmhjbHVzdChoY2x1c3QoY29yX3BlYXJzb25fZ2VuZSksIGsgPSA3KQpgYGAKCgotIERlc3NpbmV6IHVuZSBoZWF0bWFwIGR1IHLDqXN1bHRhdCwgZW4gc8OpbGVjdGlvbm5hbnQgbGVzIGRldXggcsOpc3VsdGF0cyBkZSBjbHVzdGVyaW5nIGNpLWRlc3N1cyBwb3VyIGxlcyBnw6huZXMgZXQgbGVzIMOpY2hhbnRpbGxvbnMuIAoKYGBge3IgYmljbHVzdGVyaW5nfQpwaGVhdG1hcCh0KGZhX2V4cHJfc2VsZWN0ZWQpLAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iKQpgYGAKCgpJbnRlcnByw6l0ZXogbGVzIHLDqXN1bHRhdHMgZW4gcXVlbHF1ZXMgcGhyYXNlcy4gCgoKIyMjIEVucmljaGlzc2VtZW50IGZvbmN0aW9ubmVsCgpFZmZlY3R1ZXogdW5lIGFuYWx5c2UgZCdlbnJpY2hpc3NlbWVudCBmb25jdGlvbm5lbCBhdmVjIGxlcyBwcmluY2lwYXV4IGNsdXN0ZXJzIG9idGVudXMgZGFucyBsYSBzZWN0aW9uIHByw6ljw6lkZW50ZS4gCgpgYGB7ciBmdW5jdGlvbmFsX2VucmljaG1lbnR9CgpgYGAKCiMjIENvbmNsdXNpb25zIGfDqW7DqXJhbGVzCgoKUsOpc3VtZXogZW4gcXVlbHF1ZXMgcGhyYXNlcyB2b3MgY29uY2x1c2lvbnMgw6AgcGFydGlyIGRlcyByw6lzdWx0YXRzIG9idGVudXMuIAoKCgo=
=======
LS0tCnRpdGxlOiAiTWluaS1wcm9qZXQgMjAyMSAtIEV4cGxvcmF0aW9uIGRlcyBkb25uw6llcyBkZSBQYXZrb3ZpYyIKYXV0aG9yOiAiUHLDqW5vbSBOb20iCmRhdGU6ICdgciBTeXMuRGF0ZSgpYCcKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBzZWxmX2NvbnRhaW5lZDogeWVzCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogImhpZGUiCiAgcGRmX2RvY3VtZW50OgogICAgZmlnX2NhcHRpb246IHllcwogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCgpgYGB7ciBzZXR0aW5ncywgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQpvcHRpb25zKHdpZHRoID0gMzAwKQojIG9wdGlvbnMoZW5jb2RpbmcgPSAnVVRGLTgnKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDUsIAogIGZpZy5wYXRoID0gJ2ZpZ3VyZXMvbWluaS1wcm9qZXRfJywKICBmaWcuYWxpZ24gPSAiY2VudGVyIiwgCiAgc2l6ZSA9ICJ0aW55IiwgCiAgZWNobyA9IFRSVUUsIAogIGV2YWwgPSBUUlVFLCAKICB3YXJuaW5nID0gRkFMU0UsIAogIG1lc3NhZ2UgPSBGQUxTRSwgCiAgcmVzdWx0cyA9IFRSVUUsIAogIGNvbW1lbnQgPSAiIikKCm9wdGlvbnMoc2NpcGVuID0gMTIpICMjIE1heCBudW1iZXIgb2YgZGlnaXRzIGZvciBub24tc2NpZW50aWZpYyBub3RhdGlvbgojIGtuaXRyOjphc2lzX291dHB1dCgiXFxmb290bm90ZXNpemUiKQpgYGAKCmBgYHtyIGxpYnJhcmllcywgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQojIExvYWQgcmVxdWlyZWQgQ1JBTiBSIGxpYnJhcmllcwpyZXF1aXJlZF9jcmFuTGliIDwtIGMoImtuaXRyIiwgCiAgICAgICAgICAgICAgICAgICAgICAiRmFjdG9NaW5lUiIsIAogICAgICAgICAgICAgICAgICAgICAgImZhY3RvZXh0cmEiLCAKICAgICAgICAgICAgICAgICAgICAgICJncHJvZmlsZXIyIiwKICAgICAgICAgICAgICAgICAgICAgICJwaGVhdG1hcCIpCmZvciAobGliIGluIHJlcXVpcmVkX2NyYW5MaWIpIHsKICBpZiAoIXJlcXVpcmUobGliLCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKGxpYiwgKQogIH0KICByZXF1aXJlKGxpYiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQp9CgpyZXF1aXJlZF9iaW9jTGliIDwtIGMoImJpb21hUnQiKQpmb3IgKGxpYiBpbiByZXF1aXJlZF9iaW9jTGliKSB7CiAgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKGxpYiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkgewogICAgQmlvY01hbmFnZXI6Omluc3RhbGwobGliKQogIH0KICByZXF1aXJlKGxpYiwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQp9CgoKa2FibGUoYXMuZGF0YS5mcmFtZShjKHJlcXVpcmVkX2NyYW5MaWIsIHJlcXVpcmVkX2Jpb2NMaWIpKSwKICAgICAgY29sLm5hbWVzID0gImxpYnJhcmllcyIsCiAgICAgIGNhcHRpb24gPSAiTG9hZGVkIHJlcXVpcmVkIGxpYnJhcmllcyIKICAgICkKCnNlc3Npb25JbmZvKCkKYGBgCgojIyBTeW5vcHNpcyBkdSBwcm9qZXQKCiMjIyBUcmF2YWlsIGRlbWFuZMOpCgpMZSBidXQgZGUgY2UgdHJhdmFpbCBlc3QgZGUgbWV0dHJlIGVuIG9ldXZyZSBsZXMgbcOpdGhvZGVzIHZ1ZXMgZGFucyBsZSBtb2R1bGUgMyAiUiBldCBzdGF0aXN0aXF1ZXMiIHBvdXIgZXhwbG9yZXIgbGUgamV1IGRlIGRvbm7DqWVzIGRlIFBhdm9rb3ZpYywgZXQgZGUgcmVuZHJlIHVuIHJhcHBvcnQgZCdhbmFseXNlIGF1IGZvcm1hdCBgLlJtZGAuIAoKTm91cyBmb3Vybmlzc29ucyBjaS1kZXNzb3VzIHVuZSB0cmFtZSBhdmVjIGxlcyBwcmluY2lwYWxlcyBzZWN0aW9ucyBhdHRlbmR1ZXMuIENlcnRhaW5lcyBjb250aWVubmVudCBkw6lqw6AgZHUgY29kZS4gVm91cyBkZXZyZXogZW4gY29tcGzDqXRlciBkJ2F1dHJlcy4gU2VudGV6LXZvdXMgbGlicmVzIGQnYWRhcHRlciBjZXR0ZSB0cmFtZSBvdSBkJ3kgYWpvdXRlciBkZXMgYW5hbHlzZXMgY29tcGzDqW1lbnRhaXJlcyBzaSBlbGxlcyB2b3VzIGFpZGVudCDDoCBpbnRlcnByw6l0ZXIgdm9zIHLDqXN1bHRhdHMuIAoKIyMjIFJlbWlzZSBkdSByYXBwb3J0CgpEYXRlOiAqKmxlIDEwIG1haSAyMDIxIG1pbnVpdCoqLiAgU2kgdm91cyBhbnRpY2lwZXogdW4gcHJvYmzDqG1lIHBvdXIgcmVtZXR0cmUgbGUgcmFwcG9ydCDDoCBjZXR0ZSBkYXRlIGNvbnRhY3Rlei1ub3VzIGF1c3NpIHJhcGlkZW1lbnQgcXVlIHBvc3NpYmxlIHBvdXIgcXVlIG5vdXMgcHVpc3Npb25zIHByw6l2b2lyIHVuZSByZW1pc2UgcGx1cyB0YXJkaXZlLiAKCi0gQ29tbWVuY2V6IHBhciByZW5vbW1lciBsZSBmaWNoaWVyIC5SbWQgZW4gcmVtcGxhw6dhbnQgUHJlbm9tLU5PTSBwYXIgdm9zIG5vbSBldCBwcsOpbm9tLiAKLSBMZSByYXBwb3J0IGVzdCBhdHRlbmR1IGVuIGZvcm1hdHMgLlJtZCArIC5IVE1MIChlbiBnYXJkYW50IGwnb3B0aW9uIHNlbGZfY29udGFpbmVkIGRlIGwnZW4tdMOqdGUgYWN0aXbDqWUpLiAKLSBEw6lwb3NleiBsZXMgZmljaGllcnMgZGFucyB1biBzb3VzLWRvc3NpZXIgZGUgdm90ZSBjb21wdGUgZHUgY2x1c3Rlci4gQXR0ZW50aW9uLCB2ZWlsbGV6IMOgIHJlc3BlY3RlciBwcsOpY2lzw6ltZW50IGNldHRlIHN0cnVjdHVyZSBkZSBjaGVtaW4gY2FyIG5vdXMgbm91cyBiYXNlcm9ucyBkZXNzdXMgcG91ciByw6ljdXDDqXJlciB2b3MgcsOpc3VsdGF0cy4gCgogICAgYC9zaGFyZWQvcHJvamVjdHMvZHViaWkyMDIxL1tsb2dpbl0vbTMtc3RhdC1SL21pbmktcHJvamV0YCAKCiMjIyBDcml0w6hyZXMgZCfDqXZhbHVhdGlvbgoKLSBSZXByb2R1Y3RpYmlsaXTDqSBkZXMgYW5hbHlzZXM6IG5vdXMgdGVudGVyb25zIGRlIHJlZ8OpbsOpcmVyIGxlIHJhcHBvcnQgSFRNTCDDoCBwYXJ0aXIgZGUgdm90cmUgUm1kLCBlbiBwYXJ0YW50IGRlIG5vdHJlIGNvbXB0ZSBzdXIgbGUgc2VydmV1ciBJRkIuIAotIE1hbmlwdWxhdGlvbiBkZXMgb2JqZXRzIFIKLSBNb2JpbGlzYXRpb24gZGVzIG3DqXRob2RlcyBzdGF0aXN0aXF1ZXMgdnVlcyBhdSBjb3VycwotIFBlcnRpbmVuY2UgZGVzIGludGVycHLDqXRhdGlvbnMgc3RhdGlzdGlxdWVzCi0gUGVydGluZW5jZSBkZXMgaW50ZXJwcsOpdGF0aW9ucyBiaW9sb2dpcXVlcwotIENsYXJ0w6kgZGUgbGEgcsOpZGFjdGlvbgotIENsYXJ0w6kgZGVzIGlsbHVzdHJhdGlvbnMgKGZpZ3VyZXMgZXQgdGFibGVhdXgpOiBncmFwaGlzbWVzLCBsw6lnZW5kZXMgLi4uCgpOb3VzIHZvdXMgZW5jb3VyYWdlb25zIMOgIGFzc3VyZXIgbGEgbGlzaWJpbGl0w6kgZGUgdm90cmUgY29kZSAoc3ludGF4ZSwgbm9tbWFnZSBkZXMgdmFyaWFibGVzLCBjb21tZW50YWlyZXMgZGUgY29kZSkKCiMjIyBPYmplY3RpZnMgc2NpZW50aWZpcXVlcwoKTm91cyBwYXJ0b25zIGR1IG3Dqm1lIGpldSBkZSBkb25uw6llcyAqRmlsIFJvdWdlKiBkZSBjZSBtb2R1bGUgaXNzdWVzIGRlIGxhIHB1YmxpY2F0aW9uIFBhdmtvdmljLCBNLiwgUGFudGFubywgTC4sIEdlcmxhY2gsIEMuVi4gZXQgYWwuIE11bHRpIG9taWNzIGFuYWx5c2lzIG9mIGZpYnJvdGljIGtpZG5leXMgaW4gdHdvIG1vdXNlIG1vZGVscy4gU2NpIERhdGEgNiwgOTIgKDIwMTkpLiBodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zNDE1OTctMDE5LTAwOTUtNQoKKipSYXBwZWwgc3VyIGxlcyDDqWNoYW50aWxsb25zOioqCgpEZXV4IG1vZMOobGVzIGRlIGZpYnJvc2UgcsOpbmFsZSBjaGV6IGxhIHNvdXJpcyBzb250IMOpdHVkacOpczoKCjEuIExlIHByZW1pZXIgZXN0IHVuIG1vZMOobGUgZGUgbsOpcGhyb3BhdGhpZSByw6l2ZXJzaWJsZSBpbmR1aXRlIHBhciBsJ2FjaWRlIGZvbGlxdWUgKGZvbGljIGFjaWQgKEZBKSkuIExlcyBzb3VyaXMgb250IMOpdMOpIHNhY3JpZmnDqWVzIGF2YW50IGxlIHRyYWl0ZW1lbnQgKG5vcm1hbCksIHB1aXMgw6Agam91ciAxLCAyLCA3IGV0IDE0IChkYXkxLC4uLikgYXByw6hzIHVuZSBzZXVsZSBpbmplY3Rpb24gZCdhY2lkZSBmb2xpcXVlLgoKMi4gTGUgc2Vjb25kIGVzdCB1biBtb2TDqGxlIGlycsOpdmVyc2libGUgaW5kdWl0IGNocmlydXJnaWNhbGVtZW50ICh1bmlsYXRlcmFsIHVyZXRlcmFsIG9ic3RydWN0aW9uIChVVU8pKS4gbGVzIHNvdXJpcyBvbnQgw6l0w6kgc2FjcmlmacOpZXMgYXZhbnQgb2JzdHJ1Y3Rpb24gKGRheSAwKSBldCDDoCAzLCA3IGV0IDE0IGpvdXJzIGFwcsOocyBvYnN0cnVjdGlvbiBwYXIgbGlnYXRpb24gZGUgbCd1cmV0w6hyZSBkdSByZWluIGdhdWNoZS4KCkEgcGFydGlyIGRlIGNlcyBleHRyYWl0cyBkZSByZWluLCBsJ0FSTiBtZXNzYWdlciB0b3RhbCBldCBsZXMgcGV0aXRzIEFSTnMgb250IMOpdMOpIHPDqXF1ZW5jw6lzIGV0IGxlcyBwcm90w6lpbmVzIGNhcmF0w6lyaXPDqWVzIHBhciBzcGVjdHJvbcOpdHJpZSBkZSBtYXNzZSBlbiB0YW5kZW0gKFRNVCkuCgoqKkJ1dCBzY2llbnRpZmlxdWU6KiogRGFucyBsZSB0dXRvcmllbCBzdXIgbGVzIGRhdGFmcmFtZXMsIHZvdXMgYXZleiB0cmF2YWlsbMOpIHN1ciBsZXMgZG9ubsOpZXMgZGUgKioqdHJhbnNjcmlwdG9tZSBkdSBtb2TDqGxlIFVVTyoqKi4gRGFucyBjZSBtaW5pLXByb2pldCwgdm91cyBhbGxleiB0cmF2YWlsbGVyIHN1ciBsZXMgZG9ubsOpZXMgZHUgKioqdHJhbnNjcmlwdG9tZSBkdSBtb2TDqGxlIEZBKioqIGFmaW4gZGUgcmVncm91cGVyIGxlcyBvYnNlcnZhdGlvbnMgKMOpY2hhbnRpbGxvbikgZXQgbGVzIGfDqG5lcyBzZWxvbiBkZXMgcHJvZmlscyBkJ2V4cHJlc3Npb24gc2ltaWxhaXJlcy4KCioqVm90cmUgcHJvamV0IHNlIGTDqWNvbXBvc2UgZW4gNCBwYXJ0aWVzOioqCgoxLiBzdGF0c2l0aXF1ZXMgZGVzY3JpcHRpdmVzIGRlcyBkb25uw6llcyBicnV0ZXM6IGNvbW1hbmRlcyBmb3VybmllcwoyLiBub3JtYWxpc2F0aW9uIGRlcyBkb25uw6llcyA6IGNvbW1hbmRlcyBmb3VybmllcwozLiBzdGF0aXN0aXF1ZXMgZGVzY3JpcHRpdmVzIGRlcyBkb25uw6llcyBub3JtYWxpc8OpZXM6IMOgIHZvdXMgZGUgam91ZXIKNC4gYW5hbHlzZSBkZSByZWdyb3VwZW1lbnQgZGVzIGRvbm7DqWVzOiDDoCB2b3VzIGRlIGpvdWVyCgojIyAxLiBMZXMgZG9ubsOpZXMgYnJ1dGVzCgoqKipWb3VzIG4nYXZleiByaWVuIMOgIGNvZGVyIGljaS4gTGUgY29kZSBlc3QgZm91cm5pLioqKgoKIyMjIENoYXJnZW1lbnQgZGVzIGRvbm7DqWVzIGJydXRlcwoKTGUgYmxvYyBzdWl2YW50IGNvbnRpZW50IHVuZSBmb25jdGlvbiBxdWkgcGVybWV0IGRlIHTDqWzDqWNoYXJnZXIgdW4gZmljaGllciBkYW5zIGwnZXNwYWNlIGRlIHRyYXZhaWwsIHNhdWYgcydpbCBlc3QgZMOpasOgIHByw6lzZW50LiBOb3VzIGwndXRpbGlzZXJvbnMgZW5zdWl0ZSBwb3VyIHTDqWzDqWNoYXJnZXIgbGVzIGRvbm7DqWVzIMOgIGFuYWx5c2VyIGVuIMOpdml0YW50IGRlIHJlZmFpcmUgbGUgdHJhbnNmZXJ0IMOgIGNoYXF1ZSBleMOpY3V0aW9uIGRlIGwnYW5hbHlzZS4gCgpgYGB7ciBmdW5jdGlvbl9kb3dubG9hZF9vbmx5X29uY2V9CiMnIEB0aXRsZSBEb3dubG9hZCBhIGZpbGUgb25seSBpZiBpdCBpcyBub3QgeWV0IGhlcmUKIycgQGF1dGhvciBKYWNxdWVzIHZhbiBIZWxkZW4gZW1haWx7SmFjcXVlcy52YW4tSGVsZGVuQEBmcmFuY2UtYmlvaW5mb3JtYXRpcXVlLmZyfQojJyBAcGFyYW0gdXJsX2Jhc2UgYmFzZSBvZiB0aGUgVVJMLCB0aGF0IHdpbGwgYmUgcHJlcGVuZGVkIHRvIHRoZSBmaWxlIG5hbWUKIycgQHBhcmFtIGZpbGVfbmFtZSBuYW1lIG9mIHRoZSBmaWxlIChzaG91bGQgbm90IGNvbnRhaW4gYW55IHBhdGgpCiMnIEBwYXJhbSBsb2NhbF9mb2xkZXIgcGF0aCBvZiBhIGxvY2FsIGZvbGRlciB3aGVyZSB0aGUgZmlsZSBzaG91bGQgYmUgc3RvcmVkCiMnIEByZXR1cm4gdGhlIGZ1bmN0aW9uIHJldHVybnMgdGhlIHBhdGggb2YgdGhlIGxvY2FsIGZpbGUsIGJ1aWx0IGZyb20gbG9jYWxfZm9sZGVyIGFuZCBmaWxlX25hbWUKIycgQGV4cG9ydMKpCmRvd25sb2FkX29ubHlfb25jZSA8LSBmdW5jdGlvbigKICB1cmxfYmFzZSwgCiAgZmlsZV9uYW1lLAogIGxvY2FsX2ZvbGRlcikgewoKICAjIyBEZWZpbmUgdGhlIHNvdXJjZSBVUkwgIAogIHVybCA8LSBmaWxlLnBhdGgodXJsX2Jhc2UsIGZpbGVfbmFtZSkKICBtZXNzYWdlKCJTb3VyY2UgVVJMXG5cdCIsICB1cmwpCgogICMjIERlZmluZSB0aGUgbG9jYWwgZmlsZQogIGxvY2FsX2ZpbGUgPC0gZmlsZS5wYXRoKGxvY2FsX2ZvbGRlciwgZmlsZV9uYW1lKQogIAogICMjIENyZWF0ZSB0aGUgbG9jYWwgZGF0YSBmb2xkZXIgaWYgaXQgZG9lcyBub3QgZXhpc3QKICBkaXIuY3JlYXRlKGxvY2FsX2ZvbGRlciwgc2hvd1dhcm5pbmdzID0gRkFMU0UsIHJlY3Vyc2l2ZSA9IFRSVUUpCiAgCiAgIyMgRG93bmxvYWQgdGhlIGZpbGUgT05MWSBpZiBpdCBpcyBub3QgYWxyZWFkeSB0aGVyZQogIGlmICghZmlsZS5leGlzdHMobG9jYWxfZmlsZSkpIHsKICAgIG1lc3NhZ2UoIkRvd25sb2FkaW5nIGZpbGUgZnJvbSBzb3VyY2UgVVJMIHRvIGxvY2FsIGZpbGVcblx0IiwgCiAgICAgICAgICAgIGxvY2FsX2ZpbGUpCiAgICBkb3dubG9hZC5maWxlKHVybCA9IHVybCwgZGVzdGZpbGUgPSBsb2NhbF9maWxlKQogIH0gZWxzZSB7CiAgICBtZXNzYWdlKCJMb2NhbCBmaWxlIGFscmVhZHkgZXhpc3RzLCBubyBuZWVkIHRvIGRvd25sb2FkXG5cdCIsIAogICAgICAgICAgICBsb2NhbF9maWxlKQogIH0KICAKICByZXR1cm4obG9jYWxfZmlsZSkKfQpgYGAKCk5vdXMgdMOpbMOpY2hhcmdlb25zIGRldXggZmljaGllcnMgZGFucyB1biBkb3NzaWVyIGxvY2FsIGB+L20zLXN0YXQtUi9wYXZrb3ZpY19hbmFseXNpc2AgKioodm91cyBwb3V2ZXogY2hhbmdlciBsZSBub20gb3UgY2hlbWluIGRhbnMgbGUgY2h1bmsgY2ktZGVzc291cykqKiwgZXQgbGVzIGNoYXJnZW9ucyBkYW5zIGxlcyBkYXRhLmZyYW1lcyBzdWl2YW50czogCgotIERvbm7DqWVzIGJydXRlcyBkZSB0cmFuc2NyaXB0b21lOiBgZmFfZXhwcl9yYXdgCi0gTcOpdGFkb25uw6llczogYGZhX21ldGFgCgpgYGB7ciBkb3dubG9hZF9hbmRfbG9hZH0KIyMgRGVmaW5lIHRoZSByZW1vdGUgVVJMIGFuZCBsb2NhbCBmb2xkZXIKcGF2a292aWNfdXJsIDwtICJodHRwczovL2dpdGh1Yi5jb20vRFUtQmlpL21vZHVsZS0zLVN0YXQtUi9yYXcvbWFzdGVyL3N0YXQtUl8yMDIxL2RhdGEvcGF2a292aWNfMjAxOS8iCgojIyBEZWZpbmUgdGhlIGxvY2FsIGZvbGRlciBmb3IgdGhpcyBhbmFseXNpcyAod2hlcmUgdGhlIGRhdGEgd2lsbCBiZSBkb3dubG9hZGVkIGFuZCB0aGUgcmVzdWx0cyBnZW5lcmF0ZWQpCnBhdmtvdmljX2ZvbGRlciA8LSAifi9tMy1zdGF0LVIvcGF2a292aWNfYW5hbHlzaXMiCgojIyBEZWZpbmUgYSBzdWItZm9sZGVyIGZvciB0aGUgZGF0YQpwYXZrb3ZpY19kYXRhX2ZvbGRlciA8LSBmaWxlLnBhdGgocGF2a292aWNfZm9sZGVyLCAiZGF0YSIpCgojIyBEb3dubG9hZCBhbmQgbG9hZCB0aGUgZXhwcmVzc2lvbiBkYXRhIHRhYmxlCiMjIE5vdGU6IHdlIHVzZSBjaGVjay5uYW1lcz1GQUxTRSB0byBhdm9pZCByZXBsYWNpbmcgaHlwaGVucyBieSBkb3RzCiMjIGluIHNhbXBsZSBuYW1lcywgYmVjYXVzZSB3ZSB3YW50IHRvIGtlZXAgdGhlbSBhcyBpbiB0aGUgCiMjIG9yaWdpbmFsIGRhdGEgZmlsZXMuIAptZXNzYWdlKCJEb3dubG9hZGluZyBGQSB0cmFuc2NyaXB0b21lIGZpbGVcdCIsICJmYV9yYXdfY291bnRzLnRzdi5neiIsCiAgIlxuXHRmcm9tXHQiLCBwYXZrb3ZpY191cmwpCmZhX2V4cHJfZmlsZSA8LSBkb3dubG9hZF9vbmx5X29uY2UoCiAgdXJsX2Jhc2UgPSBwYXZrb3ZpY191cmwsIAogIGZpbGVfbmFtZSA9ICJmYV9yYXdfY291bnRzLnRzdi5neiIsCiAgbG9jYWxfZm9sZGVyID0gcGF2a292aWNfZGF0YV9mb2xkZXIpCgojIyBMb2FkIHRoZSBleHByZXNkc2lvbiB0YWJsZQptZXNzYWdlKCJMb2FkaW5nIEZBIHRyYW5zY3JpcHRvbWUgZGF0YSBmcm9tXG5cdCIsIGZhX2V4cHJfZmlsZSkKZmFfZXhwcl9yYXcgPC0gcmVhZC5kZWxpbShmaWxlID0gZmFfZXhwcl9maWxlLCAKICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxKQoKIyMgRG93bmxvYWQgdGhlIG1ldGFkYXRhIGZpbGUKbWVzc2FnZSgiRG93bmxvYWRpbmcgRkEgbWV0YWRhdGEgZmlsZVx0IiwgImZhX3RyYW5zY3JpcHRvbWVfbWV0YWRhdGEudHN2IiwKICAiXG5cdGZyb21cdCIsIHBhdmtvdmljX3VybCkKZmFfbWV0YV9maWxlIDwtIGRvd25sb2FkX29ubHlfb25jZSgKICB1cmxfYmFzZSA9IHBhdmtvdmljX3VybCwgCiAgZmlsZV9uYW1lID0gImZhX3RyYW5zY3JpcHRvbWVfbWV0YWRhdGEudHN2IiwKICBsb2NhbF9mb2xkZXIgPSBwYXZrb3ZpY19kYXRhX2ZvbGRlcikKCiMjIExvYWQgdGhlIG1ldGFkYXRhCm1lc3NhZ2UoIkxvYWRpbmcgRkEgbWV0YWRhdGEgZnJvbVxuXHQiLCBmYV9tZXRhX2ZpbGUpCmZhX21ldGEgPC0gcmVhZC5kZWxpbShmaWxlID0gZmFfbWV0YV9maWxlLCAKICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSAxKQpgYGAKCk5vdXMgcmVnYXJkb25zIGxhIHN0cnVjdHVyZSBkZSBjaGFxdWUgZGF0YWZyYW1lLgoKYGBge3IgaW5zZXBjdCBkYXRhfQpzdHIoZmFfZXhwcl9yYXcpCnN0cihmYV9tZXRhKQpgYGAKCkxlcyBkZXV4IGZpY2hpZXJzIG5lIGRvbm5lbnQgcGFzIGxlcyBvYnNlcnZhdGlvbnMgZGUgbCfDqWNoYW50aWxsb24gZGFucyBsZSBtw6ptZSBvcmRyZToKCmBgYHtyIGNoZWNrIGRhdGEgb3JkZXJ9CmZhX21ldGEkc2FtcGxlTmFtZSA9PSBuYW1lcyhmYV9leHByX3JhdykKYGBgCgpOb3VzIGxlcyByw6lvcmdhbmlzb25zIGxlcyDDqWNoYW50aWxsb25zIGRhbnMgbCdvcmRyZSBkZSBsJ2V4cMOpcmllbmNlOiBjb25kaXRpb24gbm9ybWFsZSwgcHVpcyBkYXkgMSDDoCAxNCBhdmVjIGxlcyAzIHLDqXBsaWNhdHMuCgpgYGB7ciByZW9kZXIgZGF0YX0Kc2FtcGxlX29yZGVyIDwtIGMocGFzdGUocmVwKGMoIm5vcm1hbCIsICJkYXkxIiwgImRheTIiLCAiZGF5MyIsICJkYXk3IiwgImRheTE0IiksIGVhY2ggPSAzKSwKICAgICAgICAgICAgICAgICAgICAgICAgMTozLCBzZXAgPSAiXyIpKQoKZmFfZXhwcl9yYXcgPC0gZmFfZXhwcl9yYXdbLHNhbXBsZV9vcmRlcl0KZmFfbWV0YSA8LSBmYV9tZXRhW21hdGNoKHNhbXBsZV9vcmRlciwgZmFfbWV0YSRzYW1wbGVOYW1lKSxdCgojIFZpZXcoZmFfbWV0YSkKa2FibGUoZmFfbWV0YSwgY2FwdGlvbiA9ICJNZXRkYXRhIGZvciBQYXZrb3ZvYyBGQSB0cmFuc2NyaXB0b21lIikKYGBgCgo9PiBBaW5zaSwgbm91cyBhdm9ucyB1biBqZXUgZGUgZG9ubsOpZXMgYXZlYyB1biDDqWNoYW50aWxsb24gZGUgYHIgbnJvdyhmYV9tZXRhKWAgb2JzZXJ2YXRpb25zIGV0IGRlcyBkb25uw6llcyBkJ2V4cHJlc3Npb24gZGUgYHIgbnJvdyhmYV9leHByX3JhdylgIGfDqG5lcy4KCiMjIyBUcmFuc2Zvcm1hdGlvbiBsb2cyCgpBcHBsaXF1ZXogdW5lIHRyYW5zZm9ybWF0aW9uIGxvZzIgZGVzIGRvbm7DqWVzIGJydXRlcywgYXByw6hzIGF2b2lyIGFqb3V0w6kgdW4gZXBzaWxvbiAkXGVwc2lsb24gPSAxJCAobGVzIHZhbGV1cnMgbnVsbGVzIHNlcm9udCBkb25jIHJlcHLDqXNlbnTDqWVzIHBhciB1biBsb2cyKGNvdW50cykgdmFsYW50ICQwJC4gU3RvY2tleiBsZSByw6lzdWx0YXQgZGFucyB1biBkYXRhLmZyYW1lIG5vbW3DqSBgZmFfZXhwcl9sb2cyYC4KCkFmZmNoZXogdW4gZnJhZ21lbnQgZGVzIHRhYmxlYXV4IGBmYV9leHByX3Jhd2AgZXQgYGZhX2V4cHJfbG9nMmAgZW4gc8OpbGVjdGlvbm5hbnQgbGVzIGxpZ25lcyAxMDAgw6AgMTA5IGV0IGxlcyBjb2xvbm5lcyA1IMOgIDEwLCBhZmluIGRlIHZvdXMgYXNzdXJlciBxdWUgbGEgdHJhbnNmb3JtYXRpb24gbG9nMiBhIGJpZW4gZm9uY3Rpb25uw6kuIAoKYGBge3IgbG9nMl90cmFuc2Zvcm19CiMjIExvZzIgdHJhbnNmb3JtYXRpb24gb2YgdGhlIHRyYW5zY3JpcHRvbWUgZGF0YQplcHNpbG9uIDwtIDEKZmFfZXhwcl9sb2cyIDwtIGxvZzIoZmFfZXhwcl9yYXcgKyBlcHNpbG9uKQojIGRpbShmYV9leHByX2xvZzIpCiMgVmlldyhoZWFkKGZhX2V4cHJfbG9nMikpCgojIyBEaXNwbGF5IG9mIGEgZnJhZ21lbnQgb2YgdGhlIGRhdGEgYmVmb3JlIGFuZCBhZnRlciBsb2cyIHRyYW5zZm9ybWF0aW9uCmthYmxlKGZhX2V4cHJfcmF3WzEwMDoxMDksIDU6MTBdLCBjYXB0aW9uID0gIkZyYWdtZW50IGRlcyBkb25uw6llcyB0cmFuc2NyaXB0b21pcXVlcyBicnV0ZXMiKQprYWJsZShmYV9leHByX2xvZzJbMTAwOjEwOSwgNToxMF0sIGNhcHRpb24gPSAiRnJhZ21lbnQgZGVzIGRvbm7DqWVzIHRyYW5zY3JpcHRvbWlxdWVzIGFwcsOocyB0cmFuc2Zvcm1hdGlvbiBsb2cyIikKYGBgCgojIyMgU3RhdGlzdGlxdWVzIGRlc2NyaXB0aXZlcwoKRGFucyBsZSB0dXRvcmlhbCBzdXIgbGVzIGRhdGFmcmFtZXMgc3VyIGxlIGpldSBkZSBkb25uw6llcyAidXVvIiAocmVsaXNleiBsZSBjb3JyaWfDqSksIG5vdXMgdm91cyBhdm9ucyBkZW1hbmTDqSBkZSBjcsOpZXIgdW4gZGF0YS5mcmFtZSBxdWkgY29sbGVjdGVyYSBsZXMgc3RhdGlzdGlxdWVzIHBhciBnw6huZSBldCBwYXIgw6ljaGFudGlsbG9uLiBOb3VzIHZvdXMgZGVtYW5kb25zIGRlIHLDqWFsaXNlciB1bmUgw6l0dWRlIHNpbWlsYWlyZSBzdXIgbGVzIGRvbm7DqWVzICJGQSIuCgojIyMjIFBhciDDqWNoYW50aWxsb24gYXZhbnQgbm9ybWFsaXNhdGlvbgoKTm91cyBjcsOpb25zIHVuIGRhdGEuZnJhbWUgbm9tbcOpIGBzYW1wbGVfc3RhdF9wcmVub3JtYCBxdWkgY29tcG9ydGVyYSB1bmUgbGlnbmUgcGFyIMOpY2hhbnRpbGxvbiBldCB1bmUgY29sb25uZSBwYXIgc3RhdGlzdGlxdWUuIE5vdXMgY2FsY3Vsb25zIGxlcyBzdGF0aXN0aXF1ZXMgc3VpdmFudGVzIHN1ciBsZXMgdmFsZXVycyBsb2cyIGQnZXhwcmVzc2lvbiBkZSBjaGFxdWUgw6ljaGFudGlsbG9uOgoKLSBtb3llbm5lCi0gw6ljYXJ0LXR5cGUKLSBpbnRlcnZhbGxlIGludGVyLXF1YXJ0aWxlcwotIHByZW1pZXIgcXVhcnRpbGUKLSBtw6lkaWFuZQotIHRyb2lzacOobWUgcXVhcnRpbGUKLSBtYXhpbXVtCi0gbm9tYnJlIGRlIHZhbGV1cnMgbnVsbGVzCgpJbCBzZXJhIGFmZmljaMOpIGF2ZWMgbGEgZm9uY3Rpb24gYGthYmxlKClgIChuJ291YmxpZXogcGFzIGxhIGzDqWdlbmRlKS4gCgpgYGB7ciBzYW1wbGVfc3RhdF9wcmVfbm9ybX0KbWVzc2FnZSgiQ29tcHV0aW5nIHNhbXBsZS13aXNlIHN0YXRpc3RpY3Mgb24gcmF3IGNvdW50cyIpCnNhbXBsZV9zdGF0X3ByZW5vcm0gPC0gZGF0YS5mcmFtZSgKICBtZWFuID0gYXBwbHkoZmFfZXhwcl9sb2cyLCAyLCBtZWFuLCBuYS5ybT1UUlVFKSwKICBzZCA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMiwgc2QsIG5hLnJtPVRSVUUpLAogIGlxciA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMiwgSVFSLCBuYS5ybT1UUlVFKSwKICBRMSA9IGFwcGx5KGZhX2V4cHJfbG9nMiwgMiwgcXVhbnRpbGUsIHAgPSAwLjI1LCBuYS5ybT1UUlVFKSwKICBtZWRpYW4gPSBhcHBseShmYV9leHByX2xvZzIsIDIsIG1lZGlhbiwgbmEucm09VFJVRSksCiAgUTMgPSBhcHBseShmYV9leHByX2xvZzIsIDIsIHF1YW50aWxlLCBwID0gMC43NSwgbmEucm09VFJVRSksCiAgbWF4ID0gYXBwbHkoZmFfZXhwcl9sb2cyLCAyLCBtYXgsIG5hLnJtPVRSVUUpLAogIG51bGwgPSBhcHBseShmYV9leHByX2xvZzIgPT0gMCwgMiwgc3VtLCBuYS5ybT1UUlVFKQopCgprYWJsZShzYW1wbGVfc3RhdF9wcmVub3JtLCBjYXB0aW9uID0gIlNhbXBsZS13aXNlIHN0YXRpc3RpY3MgYmVmb3JlIG5vcm1hbGlzYXRpb24uIikKCmBgYAoKIyMjIyBQYXIgZ8OobmUgYXZhbnQgbm9ybWFsaXNhdGlvbgoKTm91cyBjcsOpb25zIGNpLWRlc3NvdXMgdW4gZGF0YS5mcmFtZSBub21tw6kgYGdlbmVfc3RhdF9wcmVub3JtYCBxdWkgY29tcG9ydGVyYSB1bmUgbGlnbmUgcGFyIGfDqG5lIGV0IHVuZSBjb2xvbm5lIHBhciBzdGF0aXN0aXF1ZS4gTm91cyBjYWxjdWxvbnMgbGVzIHN0YXRpc3RpcXVlcyBzdWl2YW50ZXMgc3VyIGxlcyB2YWxldXJzIGxvZzIgZGUgY2hhcXVlIGfDqG5lLgoKLSBtb3llbm5lCi0gbcOpZGlhbmUKLSDDqWNhcnQtdHlwZQotIHByZW1pZXIgcXVhcnRpbGUKLSB0cm9pc2nDqG1lIHF1YXJ0aWxlCi0gbWF4aW11bQotIG5vbWJyZSBkZSB2YWxldXJzIG51bGxlcwotIGludGVydmFsbGUgaW50ZXItcXVhcnRpbGVzCgpDZXMgcsOpc3VsdGF0cyBzZXJvbnQgc3RvY2vDqXMgZGFucyB1biBkYXRhLmZyYW1lIGF2ZWMgMSBsaWduZSBwYXIgw6ljaGFudGlsbG9uIGV0IDEgY29sb25uZSBwYXIgc3RhdGlzdGlxdWUuIFZvdXMgYWZmaWNoZXJleiBsZXMgbGlnbmVzIDEwMCDDoCAxMDkgZGUgY2UgdGFibGVhdSBkZSBzdGF0aXN0aXF1ZXMgYXZlYyBsYSBmb25jdGlvbiBga2FibGUoKWAgKG4nb3VibGlleiBwYXMgbGEgbMOpZ2VuZGUpLgoKYGBge3IgZ2VuZV9zdGF0X3ByZV9ub3JtfQojIyBHZW5lLXdpc2Ugc3RhdGlzdGljcyBmb3IgdGhlIHJhdyBjb3VudHMgKHdpbGwgYmUgdXNlZCBmb3Igbm9ybWFsaXNhdGlvbikKbWVzc2FnZSgiQ29tcHV0aW5nIGdlbmUtd2lzZSBzdGF0aXN0aWNzIG9uIHJhdyBjb3VudHMiKQpnZW5lX3N0YXRfcHJlbm9ybSA8LSBkYXRhLmZyYW1lKAogIG1lYW4gPSBhcHBseShmYV9leHByX3JhdywgMSwgbWVhbiwgbmEucm0gPSBUUlVFKSwKICBzZCA9IGFwcGx5KGZhX2V4cHJfcmF3LCAxLCBzZCwgbmEucm0gPSBUUlVFKSwKICBpcXIgPSBhcHBseShmYV9leHByX3JhdywgMSwgSVFSLCBuYS5ybSA9IFRSVUUpLAogIFExID0gYXBwbHkoZmFfZXhwcl9yYXcsIDEsIHF1YW50aWxlLCBwID0gMC4yNSwgbmEucm0gPSBUUlVFKSwKICBtZWRpYW4gPSBhcHBseShmYV9leHByX3JhdywgMSwgbWVkaWFuLCBuYS5ybSA9IFRSVUUpLAogIFEzID0gYXBwbHkoZmFfZXhwcl9yYXcsIDEsIHF1YW50aWxlLCBwID0gMC43NSwgbmEucm0gPSBUUlVFKSwKICBtYXggPSBhcHBseShmYV9leHByX3JhdywgMSwgbWF4LCBuYS5ybSA9IFRSVUUpLAogIG51bGwgPSBhcHBseShmYV9leHByX3JhdyA9PSAwLCAxLCBzdW0sIG5hLnJtID0gVFJVRSkKKQoKa2FibGUoZ2VuZV9zdGF0X3ByZW5vcm1bMTAwOjEwOSwgXSwgY2FwdGlvbiA9ICJHZW5lLXdpc2Ugc3RhdGlzdGljcyBiZWZvcmUgbm9ybWFsaXNhdGlvbiIpCgpgYGAKCiMjIDIuIEZpbHRyYWdlIGV0IG5vcm1hbGlzYXRpb24gZGVzIGRvbm7DqWVzCgoqKipWb3VzIG4nYXZleiByaWVuIMOgIGNvZGVyIGljaS4gTGUgY29kZSBlc3QgZm91cm5pLioqKgoKSWwgZXhpc3RlIHBsdXNpZXVycyBmYcOnb25zIGRlIG5vcm1hbGlzZXIgbGVzIGRvbm7DqWVzIGRlIHRyYW5zY3JpcHRvbWUgIHZ1ZXMgZGFucyBsZXMgbW9kdWxlcyA0IGV0IDUgKGNmLiB0b3RhbCBjb3VudHMsIHF1YW50aWxlcywgVE1NLCBSTEUsIGxpbW1hIHZvb20sLi4uKSwgbWFpcyBub3VzIGF2b25zIGNob2lzaSBpY2kgdW5lIHNvbHV0aW9uIHNpbXBsZSB0b3V0IGVuIMOpdGFudCByb2J1c3RlIHBvdXIgbm9ybWFsaXNlciBsZXMgZG9ubsOpZXMgZW4gc3RhbmRhcmRpc2FudCBsZSAzw6htZSBxdWFudGlsZS4gCgpMYSBtw6l0aG9kZSBjaG9pc2llIGljaSBjb25zaXN0ZSDDoCA6CgotIMOpY2FydGVyIGxlcyBnw6huZXMgIm5vbi1kw6l0ZWN0w6lzIiwgYydlc3Qtw6AtZGlyZSBjZXV4IGF5YW50IGRlcyB2YWxldXJzIG51bGxlcyBkYW5zIGF1IG1vaW5zIDkwJSBkZXMgw6ljaGFudGlsbG9uczsKCi0gw6ljYXJ0ZXIgbGVzIGfDqG5lcyDDoCBwZWluZSBleHByaW3DqXMsIGMnZXN0LcOgLWRpcmUgY2V1eCBheWFudCB1bmUgdmFsZXVyIG1veWVubmUgPCAxMCAoYXJiaXRyYWlyZW1lbnQpOwoKLSBzdGFuZGFyZGlzZXIgbGVzIMOpY2hhbnRpbGxvbnMgc3VyIGxlIDPDqG1lIHF1YXJ0aWxlIGRlcyB2YWxldXJzIG5vbi1udWxsZXM6IG9uIGRpdmlzZSBwYXIgbGUgM8OobWUgcXVhcnRpbGUgZGUgbCfDqWNoYW50aWxsb24gZXQgb24gbXVsdGlwbGllIHBhciBsZSAzw6htZSBxdWFydGlsZSBkw6l0ZXJtaW7DqSBzdXIgbCdlbnNlbWJsZSBkZXMgw6ljaGFudGlsbG9ucy4KCk5vdXMgZm91cm5pc3NvbnMgY2ktZGVzc291cyBsZSBjb2RlLgoKIyMjIEZpbHRyYWdlIDogw6lsaW1pbmF0aW9uIGRlcyBnw6huZXMgbm9uIGTDqXRlY3TDqXMgb3Ugw6AgcGVpbmUgZXhwcmltw6lzCgpgYGB7ciBnZW5lX2ZpbHRlcmluZ30KIyMgRGF0YSBmaWx0ZXJpbmc6IGdlbmVzIGhhdmluZyBhdCBsZWFzdCA5MCUgbnVsbCB2YWx1ZXMKbWVzc2FnZSgiRmlsdGVyaW5nIHVuZGV0ZWN0ZWQgZ2VuZXMiKQp1bmRldGVjdGVkX2dlbmVzIDwtIGdlbmVfc3RhdF9wcmVub3JtJG51bGwgPj0gbmNvbChmYV9leHByX3JhdykgKiAwLjkKcHJpbnQocGFzdGUwKCJVbmRldGVjdGVkIGdlbmVzIChudWxsIGluID49IDkwJSBzYW1wbGVzKTogIiwgc3VtKHVuZGV0ZWN0ZWRfZ2VuZXMpKSkKCiMjIERhdGEgZmlsdGVyaW5nOiBnZW5lcyBoYXZpbmcgYSBtZWFuIGV4cHJlc3Npb24gPCAxMAptZXNzYWdlKCJGaWx0ZXJpbmcgYmFyZWx5IGV4cHJlc3NlZCBnZW5lcyIpCmJhcmVseV9leHByZXNzZWRfZ2VuZXMgPC0gZ2VuZV9zdGF0X3ByZW5vcm0kbWVhbiA8IDEwCnByaW50KHBhc3RlMCgiQmFyZWx5IGV4cHJlc3NlZCBnZW5lcyAobWVhbiA8IDEwKTogIiwgc3VtKGJhcmVseV9leHByZXNzZWRfZ2VuZXMpKSkKCiMjIEFwcGx5IGZpbHRlcmluZyBvbiBib3RoIGNyaXRlcmlhCmRpc2NhcmRlZF9nZW5lcyA8LSB1bmRldGVjdGVkX2dlbmVzIHwgYmFyZWx5X2V4cHJlc3NlZF9nZW5lcwpwcmludChwYXN0ZTAoIkRpc2NhcmRlZCBnZW5lczogIiwgc3VtKGRpc2NhcmRlZF9nZW5lcykpKQprZXB0X2dlbmVzIDwtICFkaXNjYXJkZWRfZ2VuZXMKcHJpbnQocGFzdGUwKCJLZXB0IGdlbmVzOiAiLCBzdW0oa2VwdF9nZW5lcykpKQoKIyMgR2VuZXMgYWZ0ZXIgZmlsdGVyaW5nCmZhX2V4cHJfbG9nMl9maWx0ZXJlZCA8LSBmYV9leHByX2xvZzJba2VwdF9nZW5lcywgXQpgYGAKCiMjIyBOb3JtYWxpc2F0aW9uIGVudHJlIMOpY2hhbnRpbGxvbnMKCmBgYHtyIHNhbXBsZV9zdGFuZGFyZGlzYXRpb259CiMjIEdlbmVyYXRlIGEgZGF0YSBmcmFtZSB3aGVyZSBudWxsIHZhbHVlcyBhcmUgcmVwbGFjZWQgYnkgTkEKZmFfZXhwcl9ub251bGwgPC0gZmFfZXhwcl9sb2cyX2ZpbHRlcmVkCmZhX2V4cHJfbm9udWxsW2ZhX2V4cHJfbG9nMl9maWx0ZXJlZCA8PSAwXSA8LSBOQQpzdW0oaXMubmEoZmFfZXhwcl9ub251bGwpKQoKIyMgQ29tcHV0ZSB0aGUgM3JkIHF1YXJ0aWxlIG9mIG5vbi1udWxsIHZhbHVlcyBmb3IgZWFjaCBzYW1wbGUgYW5kIHN0b3JlIHRoZW0gaW4gYSB2ZWN0b3I6CnNhbXBsZV9xM19ub251bGwgPC0gYXBwbHkoZmFfZXhwcl9ub251bGwsIDIsIHF1YW50aWxlLCBwcm9iID0gMC43NSwgbmEucm0gPSBUUlVFKQojIHByaW50KHNhbXBsZV9xM19ub251bGwpCgojIyBDb21wdXRlIHRoZSBRMyBmb3IgYWxsIHRoZSB2YWx1ZXMsIHdoaWNoIHdpbGwgc2VydmUgYXMgdGFyZ2V0IHZhbHVlIGZvciB0aGUgc3RhbmRhcmRpc2VkIHNhbXBsZSBRMwphbGxfcTNfbm9udWxsIDwtIHF1YW50aWxlKHVubGlzdChmYV9leHByX25vbnVsbCksIHByb2IgPSAwLjc1LCBuYS5ybSA9IFRSVUUpCiMgcHJpbnQoYWxsX3EzX25vbnVsbCkKCiMjIFN0YW5kYXJkaXNlIGV4cHJlc3Npb24gb24gdGhlIHRoaXJkIHF1YXJ0aWxlIG9mIG5vbi1udWxsIHZhbHVlcwojIyBCZXdhcmUgOiBmb3IgdGhpcyBzdGFuZGFyZGl6YXRpb24gd2Uga2VlcCB0aGUgbnVsbCB2YWx1ZXMKIyMgVHJpY2sgOiB3ZSB0cmFuc3Bvc2UgdGhlIHRhYmxlIHRvIGFwcGx5IHRoZSByYXRpbyBzYW1wbGUgcGVyIHNhbXBsZSwgCiMjIGFuZCB0aGVuIHRyYW5zcG9zZSB0aGUgcmVzdWx0cyB0byBnZXQgYmFjayB0aGUgZ2VuZXMgaW4gcm93cyBhbmQgc2FtcGxlcyBpbiBjb2x1bW5zCmZhX2V4cHJfbG9nMl9zdGFuZGFyZCA8LSB0KHQoZmFfZXhwcl9sb2cyX2ZpbHRlcmVkKSAqIGFsbF9xM19ub251bGwgLyBzYW1wbGVfcTNfbm9udWxsICkKIyBxdWFudGlsZSh1bmxpc3QoZmFfZXhwcl9sb2cyX3N0YW5kYXJkKSwgcHJvYnMgPSAwLjc1LCBuYS5ybSA9IFRSVUUpCgojIyBXZSBhbHNvIGNvbXB1dGUgdGhlIHZhbHVlcyBmb3IgdGhlICJub251bGwiIHRhYmxlIGZvciAKIyMgdGhlIHNha2Ugb2YgY29tcGFyaXNvbiBhbmQgdG8gY2hlY2sgdGhhdCB0aGUgdGhpcmQgcXVhbnRpbGVzIG9mIG5vbi1udWxsIAojIyB2YWx1ZXMgYXJlIHdlbGwgaWRlbnRpY2FsIGFjcm9zcyBzYW1wbGVzLgpmYV9leHByX2xvZzJfc3RhbmRhcmRfbm9udWxsIDwtIHQodChmYV9leHByX25vbnVsbCkgKiBhbGxfcTNfbm9udWxsIC8gc2FtcGxlX3EzX25vbnVsbCApCiMgcXVhbnRpbGUodW5saXN0KGZhX2V4cHJfbG9nMl9zdGFuZGFyZF9ub251bGwpLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSkKCiMjIENvbXB1dGUgUTMgYmVmb3JlIGFuZCBhZnRlciBzdGFuZGFyZGlzYXRpb24sIGluY2x1ZGluZyBvciBub3QgdGhlIG51bGwgdmFsdWVzCnN0YW5kYXJkaXNhdGlvbl9pbXBhY3QgPC0gZGF0YS5mcmFtZSgKICBiZWZvcmVfYWxsID0gYXBwbHkoZmFfZXhwcl9sb2cyX2ZpbHRlcmVkLCAyLCBxdWFudGlsZSwgcHJvYiA9ICAwLjc1LCBuYS5ybSA9IFRSVUUpLAogIGJlZm9yZV9ub251bGwgPSBhcHBseShmYV9leHByX25vbnVsbCwgMiwgcXVhbnRpbGUsIHByb2IgPSAgMC43NSwgbmEucm0gPSBUUlVFKSwKICBhZnRlcl9ub251bCA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZF9ub251bGwsIDIsIHF1YW50aWxlLCBwcm9iID0gIDAuNzUsIG5hLnJtID0gVFJVRSksCiAgYWZ0ZXJfYWxsID0gYXBwbHkoZmFfZXhwcl9sb2cyX3N0YW5kYXJkLCAyLCBxdWFudGlsZSwgcHJvYiA9ICAwLjc1LCBuYS5ybSA9IFRSVUUpCikKCiMjIE5vdGU6IGFmdGVyIHN0YW5kYXJkaXphdGlvbiB0aGUgUTMgb2YgdGhlIGRhdGEgc2hvdyBzb21lIHZhcmlhdGlvbnMgCiMjIGJlY2F1c2Ugd2UgY29tcHV0ZSB0aGVtIGhlcmUgd2l0aCB0aGUgbnVsbCB2YWx1ZXMKa2FibGUoc3RhbmRhcmRpc2F0aW9uX2ltcGFjdCwgY2FwdGlvbiA9ICJJbXBhY3Qgb2Ygc3RhbmRhcmRpemF0aW9uIG9uIHRoZSB0aGlyZCBxdWFudGlsZSAoUTMpIHBlciBzYW1wbGUuIFRoaXJkIHF1YW50aWxlcyBhcmUgY29tcHV0ZWQgYmVmb3JlIGFuZCBhZnRlciBzdGFuZGFyZGlzYXRpb24sIHdpdGggZWl0aGVyIGFsbCB0aGUgdmFsdWVzIG9mIHRoZSBmaWx0ZXJlZCB0YWJsZSwgb3Igb25seSB0aGUgbm9uLW51bGwgdmFsdWVzLiAiKQpgYGAKCiMjIDMuIExlcyBkb25uw6llcyBub3JtYWxpc8OpZXMKCioqKkEgdm91cyBkZSBqb3VlciEqKioKCiMjIyBTdGF0aXN0aXF1ZXMgcGFyIGfDqG5lIGFwcsOocyBub3JtYWxpc2F0aW9uCgpHw6luw6lyZXogdW4gZGF0YS5mcmFtZSBhdmVjIHVuZSBsaWduZSBwYXIgZ8OobmUgw6AgcGFydGlyIGR1IHRhYmxlYXUgZGUgZG9ubsOpZXMgbm9ybWFsaXPDqWVzLCBhdmVjIGxlcyBzdGF0aXN0aXF1ZXMgc3VpdmFudGVzICh1bmUgc3RhdGlzdGlxdWUgcGFyIGNvbG9ubmUpOgoKLSBtb3llbm5lCi0gdmFyaWFuY2UKLSDDqWNhcnQtdHlwZQotIGNvZWZmaWNpZW50IGRlIHZhcmlhdGlvbiAow6ljYXJ0LXR5cGUgZGl2aXPDqSBwYXIgbGEgbW95ZW5uZSkKLSBpbnRlcnZhbGxlIGludGVyLXF1YXJ0aWxlcwotIG1pbmltdW0KLSBtw6lkaWFuZQotIG1heGltdW0KCmBgYHtyIGdlbmVfc3RhdF9wb3N0X25vcm19CgojIyBHZW5lLXdpc2Ugc3RhdGlzdGljcyBhZnRlciBub3JtYWxpc2F0aW9uCm1lc3NhZ2UoIkNvbXB1dGluZyBnZW5lLXdpc2Ugc3RhdGlzdGljcyBvbiBsb2cyLXRyYW5zZm9ybWVkIGFuZCBub3JtYWxpc2VkIGNvdW50cyIpCmdlbmVfc3RhdF9ub3JtIDwtIGRhdGEuZnJhbWUoCiAgbWVhbiA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCwgMSwgbWVhbiwgbmEucm0gPSBGKSwKICB2YXIgPSBhcHBseShmYV9leHByX2xvZzJfc3RhbmRhcmQsIDEsIHZhciwgbmEucm0gPSBUUlVFKSwKICBzZCA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCwgMSwgc2QsIG5hLnJtID0gRiksCiAgQ1YgPSBOQSwKICBtaW4gPSBhcHBseShmYV9leHByX2xvZzJfc3RhbmRhcmQsIDEsIG1pbiwgbmEucm0gPSBUUlVFKSwKICBRMSA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCwgMSwgcXVhbnRpbGUsIHAgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogIG1lZGlhbiA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCwgMSwgbWVkaWFuLCBuYS5ybSA9IFRSVUUpLAogIFEzID0gYXBwbHkoZmFfZXhwcl9sb2cyX3N0YW5kYXJkLCAxLCBxdWFudGlsZSwgcCA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgbWF4ID0gYXBwbHkoZmFfZXhwcl9sb2cyX3N0YW5kYXJkLCAxLCBtYXgsIG5hLnJtID0gVFJVRSksCiAgbnVsbCA9IGFwcGx5KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCA9PSAwLCAxLCBzdW0sIG5hLnJtID0gVFJVRSkKICApCmdlbmVfc3RhdF9ub3JtJENWIDwtIGdlbmVfc3RhdF9ub3JtJHNkIC8gZ2VuZV9zdGF0X25vcm0kbWVhbgoKYGBgCgojIyMgQW5ub3RhdGlvbiBkZXMgZ8OobmVzCgpDaGFxdWUgZ8OobmUgw6l0YW50IGRvbm7DqSBwYXIgc29uIGlkZW50aWZpYW50IGRhbnMgbGEgYmFzZSBkZSBkb25uw6llcyBFTlNFTUJMIHZvdXMgdXRpbGlzZXJleiBsZSBgcGFxdWV0IGJpb21hUnQgZGUgYmlvY29uZHVjdG9yYCBwb3VyIGFqb3V0ZXIgZGVzIGFubm90YXRpb25zIDogc3ltYm9sZSwgY2hyb21vc29tZSwgY29vcmRvbm7DqWVzIGfDqW5vbWlxdWVzLCBicmluLiAKU3VpdmV6IHBhcyDDoCBwYXMgbGEgbcOpdGhvZGUgcHJvcG9zw6llOgoKIC0gc8OpbGVjdGlvbm5leiBsYSBiYXNlIGRlIGRvbm7DqWVzIEVOU0VNQkwgYXZlYyBsYSBmb25jdGlvbiBgdXNlTWFydCgpYC4gQXR0ZW50aW9uIMOgIGNob2lzaXIgbGUgYm9uIGfDqW5vbWUgYXZlYyBsJ2FndW1lbnQgYGRhdGFzZXRgOiBgbW11c2N1bHVzX2dlbmVfZW5zZW1ibGAKIAogLSBhdmVjIGxhIGZvbmN0aW9uIGBnZXRCTSgpYCByw6ljdXDDqXJleiBkZSBsYSBiYXNlIGRlIGRvbm7DqWVzIEVOU0VNQkwgbGVzIGNoYW1wcyBkZW1hbmTDqXMgKCoqKnBvdXIgc3ltYm9sZSB1dGlsaXNleiBleHRlcm5hbF9nZW5lX25hbWUqKiopIGVuIGFwcGxpcXVhbnQgImVuc2VtYmxfZ2VuZWlkIiBwb3VyIGwnYWd1bWVudCBgZmlsdGVyYCBldCBlbiBpbmRpcXVhbnQgcG91ciBsJ2FyZ3VtZW50IGB2YWx1ZXNgIGxlIHZlY3RldXIgZGVzIGlkZW50aWZpYW50cyBkZXMgZ8OobmVzIHByw6lzZW50cyBkYW5zIGxlIGRhdGFmcmFtZSBgZ2VuZV9zdGF0X25vcm1gLiBWb3VzIG9idGVuZXogdW4gZGF0YWZyYW1lLgogCkEgcHLDqXNlbnQsIGFqb3V0ZXogYXUgZGF0YWZyYW1lIGBnZW5lX3N0YXRfbm9ybWAgZW4gMcOocmVzIGNvbG9ubmVzIGxlcyBhbm5vdGF0aW9ucyByZXRyb3V2w6llcyBncsOiY2Ugw6AgYmlvbWFSdC4gQXR0ZW50aW9uLCBjZXJ0YWlucyBnw6huZXMgbmUgc29udCBwYXMgcmV0cm91dsOpcyBkYW5zIGxhIHZlcnNpb24gZCdFTlNFTUJMIHN1ciBiaW9tYVJ0IGRvbmMgbGFpc3NleiBkZXMgTkEgY29tbWUgZG9ubsOpZXMgbWFucXVhbnRlcyBkYW5zIGNlIGNhcy4gTm91cyB2b3VzIHJlY29tbWFuZG9ucyBkJ3V0aWxpc2VyIGxhIGZ1bmN0aW9uIGBtZXJnZSgpYCBvdSBgbGVmdF9qb2luKClgIGRlIGBkcGx5cmAgcG91ciBmdXNpb25uZXIgbGVzIGRldXggZGF0YWZyYW1lcyBlbiB1biBzZXVsLgogCmBgYHtyIGdlbmVfYW5ub3RhdGlvbnN9CiMjIyBHZW5lIGFubm90YXRpb25zICMjIyMKCiMjIE9wZW4gYSBjb25uZWN0aW9uIHRvIEVuc2VtYmwgTUFSVAptZXNzYWdlKCJPcGVuaW5nIGNvbm5lY3Rpb24gdG8gRW5zZW1ibCBNQVJUIikKZW5zZW1ibCA8LSB1c2VNYXJ0KCJFTlNFTUJMX01BUlRfRU5TRU1CTCIsIAogICAgICAgICAgICAgICAgICAgaG9zdCA9ICJ3d3cuZW5zZW1ibC5vcmciLCAKICAgICAgICAgICAgICAgICAgIGRhdGFzZXQgPSAibW11c2N1bHVzX2dlbmVfZW5zZW1ibCIpCgojIyBHZXQgZ2VuZSBhbm5vdGF0aW9ucwptZXNzYWdlKCJHZXR0aW5nIGdlbmUgYW5ub3RhdGlvbnMiKQpnZW5lcyA8LSBnZXRCTShhdHRyaWJ1dGVzID0gYygiZW5zZW1ibF9nZW5lX2lkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHRlcm5hbF9nZW5lX25hbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNocm9tb3NvbWVfbmFtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdGFydF9wb3NpdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZW5kX3Bvc2l0aW9uIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJhbmQiKSwgCiAgICAgICAgICAgICAgIGZpbHRlciA9ICJlbnNlbWJsX2dlbmVfaWQiLAogICAgICAgICAgICAgICB2YWx1ZXMgPSByb3cubmFtZXMoZ2VuZV9zdGF0X25vcm0pLAogICAgICAgICAgICAgICBtYXJ0ID0gZW5zZW1ibCkKICAKIyMgTWVyZ2UgZ2VuZSBhbm5vdGF0aW9ucyBhbmQgZXhwcmVzc2lvbiBzdGF0aXN0aWNzCmdlbmVfc3RhdF9ub3JtIDwtIG1lcmdlKGdlbmVzLCBnZW5lX3N0YXRfbm9ybSwgYnkueCA9ICJlbnNlbWJsX2dlbmVfaWQiLCBieS55ID0gMCwgc29ydCA9IEZBTFNFKQoKa2FibGUoZ2VuZV9zdGF0X25vcm1bMTAwOjEwOSwgXSwgY2FwdGlvbiA9ICJHZW5lLXdpc2Ugc3RhdGlzdGljcyBhZnRlciBub3JtYWxpc2F0aW9uIikKYGBgCgoqKkNoYWxsZW5nZSBmYWxjdWx0YXRpZjoqKgoKRW5maW4sIHLDqW9yZG9ubmV6IGxlcyBnw6huZXMgcGFyIHBvc2l0aW9uIGfDqW5vbWlxdWUgZXQgYWZmaWNoZXogbGVzIGxpZ25lcyA1IHByZW1pw6hyZXMgZXQgIDUgZGVybmnDqHJlcyBsaWduZXMgZGUgY2UgdGFibGVhdSBkZSBzdGF0aXN0aXF1ZXMuIAoKYGBge3IgZGlzcGxheSBmcmFnbWVudCBvZiBnZW5lLXdpc2Ugc3RhdHMgYWZ0ZXIgbm9ybX0KCmdlbmVfc3RhdF9ub3JtIDwtIGdlbmVfc3RhdF9ub3JtW29yZGVyKGdlbmVfc3RhdF9ub3JtJGNocm9tb3NvbWVfbmFtZSwgZ2VuZV9zdGF0X25vcm0kc3RhcnRfcG9zaXRpb24pLF0KCmthYmxlKGdlbmVfc3RhdF9ub3JtW2MoMTo1LCAobnJvdyhnZW5lX3N0YXRfbm9ybSkgLSA0KTpucm93KGdlbmVfc3RhdF9ub3JtKSksIF0sIAogICAgICBjYXB0aW9uID0gIkdlbmUtd2lzZSBzdGF0aXN0aWNzIGFmdGVyIG5vcm1hbGlzYXRpb24uIikKCmBgYAoKCiMjIyBEaXN0cmlidXRpb24gZGVzIGRvbm7DqWVzCgotIERlc3NpbmV6IHNvdXMgZm9ybWUgZCd1biBoaXN0b2dyYW1tZSBsYSBkaXN0cmlidXRpb24gZGVzIHZhbGV1cnMgYXByw6hzIG5vcm1hbGlzYXRpb24gKHRvdXMgw6ljaGFudGlsbG9ucyBjb25mb25kdXMpCgpgYGB7ciBmYV9leHByX25vcm1fZGlzdHJpYiwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9NSwgb3V0LndpZHRoPSI3MCUiLCBmaWcuY2FwPSJEaXN0cmlidXRpb24gb2YgZXhwcmVzc2lvbiB2YWx1ZXMgKGxvZzIgY291bnRzKSBhZnRlciBnZW5lIGZpbHRlcmluZyBhbmQgc3RhbmRhcmRpc2F0aW9uIG9uIHRoZSBzYW1wbGUtd2lzZSB0aGlyZC1xdWFydGlsZSBvZiBub24tbnVsbCB2YWx1ZXMuIFRoZSB2ZXJ0aWNhbCBsaW5lIGhpZ2hsaWdodHMgdGhlIG1lYW4gdmFsdWUuICJ9Cmhpc3QodW5saXN0KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCksIAogICAgIGJyZWFrcyA9IHNlcShmcm9tID0gMCwgdG8gPSBtYXgoZmFfZXhwcl9sb2cyX3N0YW5kYXJkKSArIDEsIGJ5ID0gMC4yNSksCiAgICAgeGxhYiA9ICJsb2cyKGNvdW50cykgYWZ0ZXIgc3RhbmRhcmRpc2F0aW9uIiwgCiAgICAgeWxhYiA9ICJudW1iZXIgb2YgZ2VuZXMgYWZ0ZXIgZmlsdGVyaW5nIiwKICAgICBjb2wgPSAiI0JCRERGRiIsCiAgICAgbGFzID0gMSwgY2V4LmF4aXMgPSAwLjgsCiAgICAgbWFpbiA9ICJkaXN0cmlidXRpb24gYWZ0ZXIgc3RhbmRhcmRpc2F0aW9uIikKYWJsaW5lKHYgPSBtZWFuKGZhX2V4cHJfbG9nMl9zdGFuZGFyZCksIGNvbCA9ICJkYXJrZ3JlZW4iLCBsd2QgPSAyKQoKYGBgCgotIERlc3NpbmV6IHVuIGJveCBwbG90IGRlcyDDqWNoYW50aWxsb25zIGF2YW50IGV0IGFwcsOocyBub3JtYWxpc2F0aW9uLCBldCBjb21tZW50ZXogbGEgZmHDp29uIGRvbnQgbCdlZmZldCBkZSBsYSBub3JtYWxpc2F0aW9uIGFwcGFyYcOudCBzdXIgY2VzIGdyYXBoaXF1ZXMuIAoKYGBge3IgYm94cGxvdHNfc3RhbmRhcmRpc2F0aW9uX2ltcGFjdCwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyLCBvdXQud2lkdGg9IjEwMCUiLCBmaWcuY2FwPSJCb3ggcGxvdHMgc2hvd2luZyB0aGUgaW1wYWN0IG9mIG5vcm1hbGlzYXRpb24ifQojIyMjIEJveCBwbG90cyB0byBzaG93IG5vcm1hbGlzYXRpb24gaW1wYWN0ICMjIyMKcGFyKG1hciA9IGMoNCw2LDQsMSkpICMjIFNldCB0aGUgbWFyZ2lucwpwYXIobWZyb3cgPSBjKDIsMikpCmJveHBsb3QoZmFfZXhwcl9sb2cyX2ZpbHRlcmVkLCAKICAgICAgICBob3Jpem9udGFsID0gVFJVRSwKICAgICAgICB4bGFiID0gImxvZzIoY291bnRzKSIsIAogICAgICAgIGxhcyA9IDEsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiQmVmb3JlIHN0YW5kYXJkaXNhdGlvblxuYWxsIHZhbHVlcyIpCmJveHBsb3QoZmFfZXhwcl9ub251bGwsIAogICAgICAgIGhvcml6b250YWwgPSBUUlVFLCAKICAgICAgICB4bGFiID0gImxvZzIoY291bnRzKSIsIAogICAgICAgIGxhcyA9IDEsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiQmVmb3JlIHN0YW5kYXJkaXNhdGlvblxuemVyb3MgZGlzY2FyZGVkIikKYm94cGxvdChmYV9leHByX2xvZzJfc3RhbmRhcmRfbm9udWxsLCAKICAgICAgICBob3Jpem9udGFsID0gVFJVRSwgCiAgICAgICAgeGxhYiA9ICJsb2cyKGNvdW50cykiLCAKICAgICAgICBsYXMgPSAxLCAKICAgICAgICBjb2wgPSBmYV9tZXRhJGNvbG9yLCAKICAgICAgICBtYWluID0gIlN0YW5kYXJkaXNlZFxuemVyb3MgZGlzY2FyZGVkIikKYm94cGxvdChmYV9leHByX2xvZzJfc3RhbmRhcmQsIAogICAgICAgIHhsYWIgPSAibG9nMihjb3VudHMpIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsIAogICAgICAgIGNvbCA9IGZhX21ldGEkY29sb3IsIAogICAgICAgIG1haW4gPSAiU3RhbmRhcmRpc2VkXG5hbGwgdmFsdWVzIikKcGFyKG1mcm93ID0gYygxLCAxKSkKcGFyKG1hciA9IGMoNCw1LDUsMSkpCgpgYGAKCiMjIDQuIEFuYWx5c2UgZGUgcmVncm91cGVtZW50IGRlcyBkb25uw6llcwoKIyMjIFPDqWxlY3Rpb24gZGUgZ8OobmVzIGQnZXhwcmVzc2lvbiDDqWxldsOpZSBldCB2YXJpYWJsZQoKUG91ciByw6lkdWlyZSBsZSBub21icmUgZGUgZ8OobmVzLCBub3VzIGFsbG9ucyDDqWNhcnRlciBsZXMgZ8OobmVzIGZhaWJsZW1lbnQgZXhwcmltw6lzIChsb2cyIG1veWVuIGluZsOpcmlldXIgw6AgNCksIGV0IG5lIHJldGVuaXIgcXVlIGNldXggcXVpIG1vbnRyZW50IGRlcyB2YXJpYXRpb25zIGltcG9ydGFudGVzIGVudHJlIMOpY2hhbnRpbGxvbnMuIFBvdXIgY2UgZGVybmllciBjcml0w6hyZSwgbm91cyBub3VzIGJhc29ucyBzdXIgbGUgY29lZmZpY2llbnQgZGUgdmFyaWF0aW9uIGFmaW4gZGUgcmVsYXRpdmlzZXIgbGEgZGlzcGVyc2lvbiAow6ljYXJ0IHR5cGUpIHBhciByYXBwb3J0IMOgIGxhIHRlbmRhbmNlIGNlbnRyYWxlIChtb3llbm5lKS4gCgo8IS0tIFPDqWxlY3Rpb25uZXogbGVzIGfDqG5lcyBheWFudCB1biBuaXZlYXUgbG9nMiBtb3llbiBtaW5pbWFsIHN1cMOpcmlldXIgw6AgMyAoJG0gPiAzJCkgZXQgdW4gY29lZmZpY2llbnQgZGUgdmFyaWF0aW9uIHN1cMOpcmlldXIgw6AgMC41ICgkQ1YgPiAwLjUkKS4gTm90ZTogY2VzIHZhbGV1cnMgc29udCBwYXJmYWl0ZW1lbnQgYXJiaXRyYWlyZXMsIGVsbGVzIG9udCDDqXTDqSBjaG9pc2llcyBwb3VyIG9idGVuaXIgdW4gbm9tYnJlIHJhaXNvbm5hYmxlIGRlIGfDqG5lcy4gIC0tPgoKU8OpbGVjdGlvbm5leiBsZXMgZ8OobmVzIGF5YW50IHVuIG5pdmVhdSBsb2cyIG1veWVuIG1pbmltYWwgc3Vww6lyaWV1ciDDoCA1ICgkbSA+IDUkKSBldCB1bmUgdmFyaWFuY2Ugc3Vww6lyaWV1cmUgw6AgMiAoJHNeMiA+IDIkKS4gTm90ZTogY2VzIHZhbGV1cnMgc29udCBwYXJmYWl0ZW1lbnQgYXJiaXRyYWlyZXMsIGVsbGVzIG9udCDDqXTDqSBjaG9pc2llcyBwb3VyIG9idGVuaXIgdW4gbm9tYnJlIHJhaXNvbm5hYmxlIGRlIGfDqG5lcy4gCgoKYGBge3IgZ2VuZV9zZWxlY3Rpb259CiMjIENvbXB1dGUgYSBCb29sZWFuIHZlY3RvciBpbmRpY2F0aW5nIHdoZXRoZXIgZWFjaCBnZW5lIHBhc3NlcyBvciBub3QgdGhlIGV4cHJlc3Npb24gbGV2ZWwgdGhyZXNob2xkCmhpZ2hfZXhwcmVzc2lvbiA8LSBnZW5lX3N0YXRfbm9ybSRtZWFuID4gNQojIHRhYmxlKGhpZ2hfZXhwcmVzc2lvbikgIyBjb3VudCBudW1iZXIgb2YgZ2VuZXMgd2l0aCBoaWdoL3dlYWsgZXhwcmVzc2lvbgoKIyMgQ29tcHV0ZSBhIEJvb2xlYW4gdmVjdG9yIGluZGljYXRpbmcgd2hldGhlciBlYWNoIGdlbmUgcGFzc2VzIG9yIG5vdCB0aGUgdmFyaWF0aW9uIGNvZWZmaWNpZW50IHRocmVzaG9sZApoaWdoX3ZhcmlhdGlvbiA8LSBnZW5lX3N0YXRfbm9ybSRDViA+IDAuNQojIHRhYmxlKGhpZ2hfdmFyaWF0aW9uKSAjIGNvdW50IG51bWJlciBvZiBnZW5lcyB3aXRoIHdlYWsgaGlnaCBjb2VmZmZpY2llbnQgb2YgdmFyaWF0aW9uCgojIyBDb21wdXRlIGEgQm9vbGVhbiB2ZWN0b3IgaW5kaWNhdGluZyB3aGV0aGVyIGVhY2ggZ2VuZSBwYXNzZXMgb3Igbm90IHRoZSB2YXJpYW5jZSB0aHJlc2hvbGQKaGlnaF92YXJpYW5jZSA8LSBnZW5lX3N0YXRfbm9ybSR2YXIgPiAyCiMgdGFibGUoaGlnaF92YXJpYW5jZSkgIyBjb3VudCBudW1iZXIgb2YgZ2VuZXMgd2l0aCB3ZWFrIGhpZ2ggdmFyaWFuY2UKCiMgIyMgU2VsZWN0IGdlbmVzIGhhdmluZyBib3RoIGEgaGlnaCBtZWFuIGV4cHJlc3Npb24gYW5kIGEgaGlnaCB2YXJpYXRpb24gY29lZmZpY2llbgojIHNlbGVjdGVkX2dlbmVzIDwtIGhpZ2hfdmFyaWF0aW9uICYgaGlnaF9leHByZXNzaW9uCiMjIFNlbGVjdCBnZW5lcyBoYXZpbmcgYm90aCBhIGhpZ2ggbWVhbiBleHByZXNzaW9uIGFuZCBhIGhpZ2ggdmFyaWFuY2UKc2VsZWN0ZWRfZ2VuZXMgPC0gaGlnaF92YXJpYW5jZSAmIGhpZ2hfZXhwcmVzc2lvbgojIHRhYmxlKHNlbGVjdGVkX2dlbmVzKSAjIGNvdW50IG51bWJlciBvZiBnZW5lcyB3aXRoIHdlYWsgaGlnaCBjb2VmZmZpY2llbnQgb2YgdmFyaWF0aW9uCnByaW50KHBhc3RlMCgiU2VsZWN0ZWQgZ2VuZXM6ICIsIHN1bShzZWxlY3RlZF9nZW5lcykpKQoKIyMgQ3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBleHByZXNzaW9uIG9mIHRoZSBzZWxlY3RlZCBnZW5lcwpmYV9leHByX3NlbGVjdGVkIDwtIGZhX2V4cHJfbG9nMl9zdGFuZGFyZFtzZWxlY3RlZF9nZW5lcywgXQoKYGBgCgpEZXNzaW5leiBkZXMgaGlzdG9ncmFtbWVzIGRlcyB2YWxldXJzIGQnZXhwcmVzc2lvbiBhdmFudCBldCBhcHLDqHMgY2V0dGUgc8OpbGVjdGlvbiBkZSBnw6huZXMsIGV0IGNvbW1lbnRleiBsZXMgZGlmZsOpcmVuY2VzLiAKCmBgYHtyIGhpc3RfZXhwcl9zZWxlY3RlZF9nZW5lcywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Niwgb3V0LndpZHRoPSIxMDAlIiwgZmlnLmNhcD0iRGlzdHJpYnV0aW9uIG9mIGV4cHJlc3Npb24gdmFsdWVzIGJlZm9yZSBhbmQgYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24ifQojIyMjIEhpc3RvZ3JhbXMgb2YgZXhwcmVzc2lvbiBiZWZvcmUgYW5kIGFmdGVyIGdlbmUgc2VsZWN0aW9uICMjIyMKCnBhcihtZnJvdyA9IGMoMiwxKSkKaGlzdCh1bmxpc3QoZmFfZXhwcl9sb2cyX3N0YW5kYXJkKSwgCiAgICAgYnJlYWtzID0gc2VxKGZyb20gPSAwLCB0byA9IG1heChmYV9leHByX2xvZzJfc3RhbmRhcmQpICsgMSwgYnkgPSAwLjI1KSwKICAgICBsYXMgPSAxLCAKICAgICBjZXguYXhpcyA9IDAuOCwgCiAgICAgbWFpbiA9ICJTdGFuZGFyZGl6ZWQgdmFsdWVzIGJlZm9yZSBnZW5lIHNlbGVjdGlvbiIsCiAgICAgY29sID0gICIjRERCQkZGIikKCmhpc3QodW5saXN0KGZhX2V4cHJfc2VsZWN0ZWQpLCAKICAgICBicmVha3MgPSBzZXEoZnJvbSA9IDAsIHRvID0gbWF4KGZhX2V4cHJfbG9nMl9zdGFuZGFyZCkgKyAxLCBieSA9IDAuMjUpLAogICAgIGxhcyA9IDEsIAogICAgIGNleC5heGlzID0gMC44LCAKICAgICBtYWluID0gIlN0YW5kYXJkaXplZCB2YWx1ZXMgYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24iLAogICAgIGNvbCA9ICAiI0ZGRERCQiIpCgpwYXIobWZyb3cgPSBjKDEsMSkpCgoKIyAjIyBTb21lIHF1aWNrIGNoZWNrczogdGhlIHNlbGVjdGlvbiBvZiBoaWdobHkgdmFyaWFibGUgZ2VuZXMgc2VsZWN0IHRob3NlIGhhdmluZyBtYW55IHplcm9zIC0gYW5kIGhpZ2ggdmFsdWVzIGluIG90aGVyIHNhbXBsZXMKIyBoaXN0KHVubGlzdChmYV9leHByX2xvZzJfZmlsdGVyZWRbaGlnaF9leHByZXNzaW9uLCBdKSwgYnJlYWtzPTEwMCkKIyBoaXN0KHVubGlzdChmYV9leHByX2xvZzJfZmlsdGVyZWRbaGlnaF92YXJpYXRpb24sIF0pLCBicmVha3M9MTAwKQojIGhpc3QodW5saXN0KGZhX2V4cHJfbG9nMl9maWx0ZXJlZFshaGlnaF92YXJpYXRpb24sIF0pLCBicmVha3M9MTAwKQojIGhpc3QodW5saXN0KGZhX2V4cHJfbG9nMl9maWx0ZXJlZFtzZWxlY3RlZF9nZW5lcywgXSksIGJyZWFrcz0xMDApCmBgYAoKRGVzc2luZXogdW4gYm94IHBsb3QgcGFyIMOpY2hhbnRpbGxvbiBkZXMgdmFsZXVycyBkJ2V4cHJlc3Npb24gYXZhbnQgZXQgYXByw6hzIHPDqWxlY3Rpb24gZGVzIGfDqG5lcywgZXQgY29tbWVudGV6IGxlIHLDqXN1bHRhdC4gCgpgYGB7ciBib3hwbG90c19leHByX3NlbGVjdGVkX2dlbmVzLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NSwgb3V0LndpZHRoPSI2MCUiLCBmaWcuY2FwPSJCb3ggcGxvdHMgb2Ygc3RhbmRhcmRpc2VkIGV4cHJlc3Npb24gdmFsdWVzIGJlZm9yZSBhbmQgYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24uICJ9CiMjIyMgSGlzdG9ncmFtIG9mIGV4cHJlc3Npb24gYWZ0ZXIgZ2VuZSBzZWxlY3Rpb24gIyMjIwoKcGFyKG1mcm93ID0gYygxLDIpKQoKYm94cGxvdChmYV9leHByX2xvZzJfc3RhbmRhcmQsIAogICAgICAgIGhvcml6b250YWwgPSBUUlVFLAogICAgICAgIHhsYWIgPSAibG9nMihjb3VudHMpIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgY29sID0gZmFfbWV0YSRjb2xvciwgCiAgICAgICAgbWFpbiA9ICJCZWZvcmUgZ2VuZSBzZWxlY3Rpb25cbnN0YW5kYXJkaXNlZCB2YWx1ZXMiKQpib3hwbG90KGZhX2V4cHJfc2VsZWN0ZWQsIAogICAgICAgIGhvcml6b250YWwgPSBUUlVFLAogICAgICAgIHhsYWIgPSAibG9nMihjb3VudHMpIiwgCiAgICAgICAgbGFzID0gMSwgCiAgICAgICAgY29sID0gZmFfbWV0YSRjb2xvciwgCiAgICAgICAgbWFpbiA9ICJBZnRlciBnZW5lIHNlbGVjdGlvblxuc3RhbmRhcmRpc2VkIHZhbHVlcyIpCgpwYXIobWZyb3cgPSBjKDEsMSkpCmBgYAoKCiMjIyBBQ1AKCkRlc3NpbmV6IHVuIHBsb3QgQUNQIGRlcyDDqWNoYW50aWxsb25zIGVuIGxlcyBjb2xvcmFudCBwYXIgY29uZGl0aW9uIGF2YW50IGV0IGFwcsOocyBub3JtYWxpc2F0aW9uLgoKLSBhdmVjIGxlcyBjb21wdGFnZXMgYnJ1dHMgZGUgbGEgbWF0cmljZSBkJ2V4cHJlc3Npb24gaW5pdGlhbGUgKCRmYV9leHByJCkKCmBgYHtyIGFjcF9yYXdfYWxsX2dlbmVzLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IlBDIHBsb3Qgb2YgdGhlIHNhbXBsZXMgZnJvbSB0aGUgcmF3IGV4cHJlc3Npb24gdmFsdWVzIG9mIGFsbCBnZW5lcy4gIn0KIyMgUmF3IGV4cHJlc3Npb24gdmFsdWVzLCBhbGwgZ2VuZXMKbWFfcGNhX3Jhd190dCA8LSBQQ0EodChmYV9leHByX3JhdyksIAogICAgICAgICAgICAgICAgICAgICAgc2NhbGUudW5pdCA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCiMgcGxvdChtYV9wY2FfcmF3X3R0LCBjaG9peCA9ICJpbmQiKQpmdml6X3BjYV9pbmQobWFfcGNhX3Jhd190dCwgY29sLmluZCA9IGZhX21ldGFbLCAiY29sb3IiXSkKCmBgYAoKPCEtLSAtIGF2ZWMgbGVzIGNvbXB0YWdlcyBicnV0cywgbWFpcyBlbiByZXN0cmVpZ25hbnQgbCdhbmFseXNlIGF1eCBnw6huZXMgc8OpbGVjdGlvbm7DqXMgKCRmYV9leHByX3Jhd1tzZWxlY3RlZF9nZW5lcyxdJCkgLS0+CgoKPCEtLSBgYGB7ciBhY3BfcmF3X3NlbGVjdGVkX2dlbmVzLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IlBDIHBsb3Qgb2YgdGhlIHNhbXBsZXMgZnJvbSB0aGUgcmF3IGV4cHJlc3Npb24gdmFsdWVzIG9mIHNlbGVjdGVkIGdlbmVzLiAifSAtLT4KPCEtLSAjIyBSYXcgdmFsdWVzIHdpdGggb25seSB0aGUgc2VsZWN0ZWQgZ2VuZXMgLS0+CjwhLS0gbWFfcGNhX3Jhd19zZWwgPC0gUENBKHQoZmFfZXhwcl9yYXdbc2VsZWN0ZWRfZ2VuZXMsXSksICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgc2NhbGUudW5pdCA9IEZBTFNFLCAgLS0+CjwhLS0gICAgICAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpIC0tPgo8IS0tIGZ2aXpfcGNhX2luZChtYV9wY2FfcmF3X3NlbCwgY29sLmluZCA9IGZhX21ldGFbLCAiY29sb3IiXSkgLS0+CjwhLS0gYGBgIC0tPgoKCi0gYXZlYyBsYSBtYXRyaWNlIGRlIHZhbGV1cnMgZmlsdHLDqWVzIGV0IGFwcsOocyB0cmFuc2Zvcm1hdGlvbiBsb2cyCgpgYGB7ciBhY3Bfbm9ybV9maWx0ZXJlZF9nZW5lcywgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OCwgb3V0LndpZHRoPSI2MCUiLCBmaWcuY2FwPSJQQyBwbG90IG9mIHRoZSBzYW1wbGVzIGZyb20gc3RhbmRhcmRpc2VkIHZhbHVlcyBiZWZvcmUgZ2VuZSBzZWxlY3Rpb24uICJ9Cm1hX3BjYV9maWx0ZXJlZCA8LSBQQ0EodChmYV9leHByX2xvZzJfZmlsdGVyZWQpLCBzY2FsZS51bml0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCiMgcGxvdChtYV9wY2FfZmlsdGVyZWQsIGNob2l4ID0gInZhciIpCiMgcGxvdChtYV9wY2Ffc2VsLCBjaG9peCA9ICJpbmQiKQpmdml6X3BjYV9pbmQobWFfcGNhX2ZpbHRlcmVkLCBjb2wuaW5kID0gZmFfbWV0YVssICJjb2xvciJdKQpgYGAKCi0gYXZlYyBsYSBtYXRyaWNlIGZpbmFsZSAodHJhbnNmb3JtYXRpb24gbG9nMiwgZmlsdHJlIGRlcyBnw6huZXMgbm9uLWTDqXRlY3TDqXMsIHN0YW5kYXJkaXNhdGlvbiBldCBzw6lsZWN0aW9uIGRlcyBnw6huZXMgZm9ydGVtZW50IGV4cHJpbcOpcyBldCDDoCBoYXV0IGNvZWZmaWNpZW50IGRlIHZhcmlhdGlvbikKCmBgYHtyIGFjcF9ub3JtX3NlbGVjdGVkX2dlbmVzLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBvdXQud2lkdGg9IjYwJSIsIGZpZy5jYXA9IlBDIHBsb3Qgb2YgdGhlIHNhbXBsZXMgZnJvbSBzdGFuZGFyZGlzZWQgdmFsdWVzIGFmdGVyIGdlbmUgc2VsZWN0aW9uLiAifQptYV9wY2Ffc2VsIDwtIFBDQSh0KGZhX2V4cHJfc2VsZWN0ZWQpLCBzY2FsZS51bml0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UpCiMgcGxvdChtYV9wY2Ffc2VsLCBjaG9peCA9ICJ2YXIiKQojIHBsb3QobWFfcGNhX3NlbCwgY2hvaXggPSAiaW5kIikKZnZpel9wY2FfaW5kKG1hX3BjYV9zZWwsIGNvbC5pbmQgPSBmYV9tZXRhWywgImNvbG9yIl0pCmBgYAoKIyMjIENsdXN0ZXJpbmcKCi0gQ2FsY3VsZXogbGVzIG1hdHJpY2VzIGRlIGRpc3RhbmNlIGVudHJlIMOpY2hhbnRpbGxvbnMsIGVuIHV0aWxpc2FudCByZXNwZWN0aXZlbWVudCBsZXMgZGlzdGFuY2VzIGV1Y2xpZGllbm5lIChgZGlzdCgpYCksIGNvZWZmaWNpZW50IGRlIFBlYXJzb24gKGBjb3IoLCBtZXRob2QgPSAicGVhcnNvbiIpYCkgIGV0IGRlIFNwZWFybWFuIChgY29yKCwgbWV0aG9kID0gInNwZWFybWFuIilgKS4KCgpgYGB7ciBzYW1wbGVfZGlzdGFuY2VzfQojIyMjIFNhbXBsZSBkaXN0YW5jZXMgIyMjIwpkaXN0X2V1Y19zZWwgPC0gZGlzdCh0KGZhX2V4cHJfc2VsZWN0ZWQpKQpjb3JfcGVhcnNvbl9zZWwgPC0gYXMuZGlzdCgxIC0gY29yKGZhX2V4cHJfc2VsZWN0ZWQpKQpjb3Jfc3BlYXJtYW5fc2VsIDwtIDEgLSBhcy5kaXN0KGNvcihmYV9leHByX3NlbGVjdGVkLCBtZXRob2QgPSAic3BlYXJtYW4iKSkKYGBgCgoKLSBFZmZlY3R1ZXogdW4gY2x1c3RlcmluZyBoacOpcmFyY2hpcXVlIGRlcyDDqWNoYW50aWxsb25zLCBlbiB1dGlsaXNhbnQgbGUgY3JpdMOocmUgZGUgV2FyZCAoYHdhcmQuZDJgKSBwb3VyIGwnYWdnbG9tw6lyYXRpb24uIENvbXBhcmV6IGxlcyBhcmJyZXMgZCfDqWNoYW50aWxsb25zIG9idGVudXMgYXZlYyBjZXMgdHJvaXMgbcOpdHJpcXVlcyBldCBjaG9pc2lzc2V6IGNlbGxlIHF1aSB2b3VzIHBhcmHDrnQgbGEgcGx1cyBwZXJ0aW5lbnRlLgoKCmBgYHtyIHNhbXBsZV9jbHVzdGVyaW5nLCBmaWcud2lkdGg9MTIsIHBsb3QuaGVpZ2h0PTUsIG91dC53aWR0aD0iMTAwJSIsIGZpZy5jYXA9IlNhbXBsZSB0cmVlIHdpdGggdGhyZWUgYWx0ZXJuYXRpdmUgZGlzdGFuY2UgbWV0cmljczogRXVjbGlkaWFudCBkaXN0YW5jZSAobGVmdCksIFBlYXJzb24gY29ycmVsYXRpb24gKGNlbnRlciksIFNwZWFybWFuIGNvcnJlbGF0aW9uIChyaWdodCkuPyAifQojIyMjIFNhbXBsZSBjbHVzdGVyaW5nICMjIyMKcGFyKG1mcm93ID0gYygxLDMpKQpwbG90KGhjbHVzdChkaXN0X2V1Y19zZWwpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJldWNsaWRlYW4gZGlzdGFuY2UiKQpwbG90KGhjbHVzdChjb3JfcGVhcnNvbl9zZWwpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJwZWFyc29uIikKcGxvdChoY2x1c3QoY29yX3NwZWFybWFuX3NlbCksIGhhbmcgPSAtMSwKICAgICBtYWluID0gInNwZWFybWFuIikKcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKCgotIEVmZmVjdHVleiB1biBjbHVzdGVyaW5nIGhpw6lyYXJjaGlxdWUgZGVzIGfDqG5lcyBlbiB1dGlsaXNhbnQgbGEgZGlzdGFuY2UgYmFzw6llIHN1ciBsZSBjb2VmZmljaWVudCBkZSBQZWFyc29uIGV0IGxlIGNyaXTDqHJlIGRlIFdhcmQKCmBgYHtyIGdlbmVfY2x1c3RlcmluZ30KY29yX3BlYXJzb25fZ2VuZSA8LSBhcy5kaXN0KDEgLSBjb3IodChmYV9leHByX3NlbGVjdGVkKSkpCnBsb3QoaGNsdXN0KGNvcl9wZWFyc29uX2dlbmUpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJnw6huZXMiKQpgYGAKCgotIERlc3NpbmV6IHVuIGFyYnJlIGF2ZWMgbGUgcsOpc3VsdGF0IGR1IGNsdXN0ZXJpbmcgZGVzIGfDqG5lcyBldCBjb21tZW50ZXogc2Egc3RyY3V0dXJlLiBTaSB2b3VzIGRldmlleiBjaG9pc2lyIGRlIGZhw6dvbiBhcmJpdHJhaXJlIHVuIG5vbWJyZSBkZSBjbHVzdGVycywgcXVlIGNob2lzaXJpZXotdm91cyA/IFBvdXJxdW9pID8gUGFzIGRlIHBhbmlxdWUsIG5vdXMgcG91dm9ucyBhc3N1bWVyIGljaSBxdWUgbGEgcsOpcG9uc2UgY29tcG9ydGUgdW5lIHBhcnQgZGUgc3ViamVjdGl2aXTDqS4gCgpgYGB7ciBnZW5lX3RyZWV9CnBsb3QoaGNsdXN0KGNvcl9wZWFyc29uX2dlbmUpLCBoYW5nID0gLTEsCiAgICAgbWFpbiA9ICJnw6huZXMiKQpyZWN0LmhjbHVzdChoY2x1c3QoY29yX3BlYXJzb25fZ2VuZSksIGsgPSA3KQpgYGAKCgotIERlc3NpbmV6IHVuZSBoZWF0bWFwIGR1IHLDqXN1bHRhdCwgZW4gc8OpbGVjdGlvbm5hbnQgbGVzIGRldXggcsOpc3VsdGF0cyBkZSBjbHVzdGVyaW5nIGNpLWRlc3N1cyBwb3VyIGxlcyBnw6huZXMgZXQgbGVzIMOpY2hhbnRpbGxvbnMuIAoKYGBge3IgYmljbHVzdGVyaW5nfQpwaGVhdG1hcCh0KGZhX2V4cHJfc2VsZWN0ZWQpLAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iKQpgYGAKCgpJbnRlcnByw6l0ZXogbGVzIHLDqXN1bHRhdHMgZW4gcXVlbHF1ZXMgcGhyYXNlcy4gCgoKIyMgNS4gRW5yaWNoaXNzZW1lbnQgZm9uY3Rpb25uZWwKCkVmZmVjdHVleiB1bmUgYW5hbHlzZSBkJ2VucmljaGlzc2VtZW50IGZvbmN0aW9ubmVsIGF2ZWMgbGVzIHByaW5jaXBhdXggY2x1c3RlcnMgb2J0ZW51cyBkYW5zIGxhIHNlY3Rpb24gcHLDqWPDqWRlbnRlLiAKCmBgYHtyIGZ1bmN0aW9uYWxfZW5yaWNobWVudH0KIyMjIyBSdW4gZW5yaWNobWVudCBhbmFseXNpcyB3aXRoIGdvc3QoKSAjIyMjCm1lc3NhZ2UoIlJ1bm5pbmcgZW5yaWNobWVudCBhbmFseXNpcyB3aXRoIGdvc3QiKQoKZ2VuZV9saXN0IDwtIHVubGlzdChzdWJzZXQoeCA9IGdlbmVfc3RhdF9ub3JtLCAKICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSBzZWxlY3RlZF9nZW5lcywKICAgICAgICAgICAgICAgICAgICBzZWxlY3QgPSBleHRlcm5hbF9nZW5lX25hbWUpKQojIGxlbmd0aChnZW5lX2xpc3QpCgpnb3N0cmVzIDwtIGdvc3QocXVlcnkgPSBnZW5lX2xpc3QsIAogICAgICAgICAgICAgICAgb3JnYW5pc20gPSAibW11c2N1bHVzIiwgCiAgICAgICAgICAgICAgICBvcmRlcmVkX3F1ZXJ5ID0gRkFMU0UsIAogICAgICAgICAgICAgICAgbXVsdGlfcXVlcnkgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICBzaWduaWZpY2FudCA9IFRSVUUsIAogICAgICAgICAgICAgICAgZXhjbHVkZV9pZWEgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICBtZWFzdXJlX3VuZGVycmVwcmVzZW50YXRpb24gPSBGQUxTRSwgCiAgICAgICAgICAgICAgICBldmNvZGVzID0gRkFMU0UsIAogICAgICAgICAgICAgICAgdXNlcl90aHJlc2hvbGQgPSAwLjA1LCAKICAgICAgICAgICAgICAgIGNvcnJlY3Rpb25fbWV0aG9kID0gImZkciIsIAogICAgICAgICAgICAgICAgZG9tYWluX3Njb3BlID0gImFubm90YXRlZCIsIAogICAgICAgICAgICAgICAgY3VzdG9tX2JnID0gTlVMTCwgCiAgICAgICAgICAgICAgICBudW1lcmljX25zID0gIiIsIAogICAgICAgICAgICAgICAgc291cmNlcyA9IE5VTEwsIAogICAgICAgICAgICAgICAgYXNfc2hvcnRfbGluayA9IEZBTFNFKQoKCiMjIENoZWNrIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIHJlc3VsdApuYW1lcyhnb3N0cmVzKQpuYW1lcyhnb3N0cmVzJHJlc3VsdCkKa2FibGUoc3VtbWFyeShnb3N0cmVzKSkKCiMgaGlzdChnb3N0cmVzJHJlc3VsdCRwX3ZhbHVlLCBicmVha3M9c2VxKGZyb209MCwgdG89MSwgYnkgPSAwLjA1KSkKCiMjIENoZWNrIHRoZSBtb3N0IHNpZ25pZmljYW50IHJlc3VsdHMsIGZvcm1hdGluZyBmb3Iga2FibGUKI2hlYWQoZ29zdHJlcyRyZXN1bHQpCmVucmljaF9vcmRlciA8LSBvcmRlcihnb3N0cmVzJHJlc3VsdCRwX3ZhbHVlLCBkZWNyZWFzaW5nID0gRkFMU0UpCnNvcnRlZF9yZXN1bHQgPC0gZ29zdHJlcyRyZXN1bHRbZW5yaWNoX29yZGVyLCBdCmthYmxlKGhlYWQoc29ydGVkX3Jlc3VsdCwgbiA9IDEwKSwgCiAgICAgIGRpZ2l0cyA9IGMoMCwgMCwgMTUsIDcsIDcsIDcsIDMsIDMsIDAsIDAsIDcsIDcsIDAsIDApLCAKICAgICAgY2FwdGlvbiA9ICJUb3AgMTAgbW9zdCBzaWduaWZpY2FudCBlbnJpY2hlZCBmdW5jdGlvbmFsIGNsYXNzZXMiKQojIGthYmxlKGhlYWQoZ29zdHJlcyRyZXN1bHQpLCAKIyAgICAgICBmb3JtYXQgPSBjKCIlcyIsICIlcyIsICIlZSIsICIlZCIsICIlZCIsICIlZCIsICIlLjNmIiwgIiUuM2YiLCAiJXMiLCAiJXMiLCAiJWQiLCAiJWQiLCAiJXMiLCAiJXMiKSkKbmFtZXMoZ29zdHJlcyRtZXRhKQoKYGBgCgojIyBDb25jbHVzaW9ucyBnw6luw6lyYWxlcwoKClLDqXN1bWV6IGVuIHF1ZWxxdWVzIHBocmFzZXMgdm9zIGNvbmNsdXNpb25zIMOgIHBhcnRpciBkZXMgcsOpc3VsdGF0cyBvYnRlbnVzLiAKCgoK
>>>>>>> b0d26ca57d19a113b96a754cdf1d9ad0a4b3d5be